Added Files

This commit is contained in:
DistractADD
2021-07-06 13:16:46 +10:00
parent f2475a3bdd
commit cfddd4fad2
393 changed files with 65842 additions and 0 deletions

View File

@@ -0,0 +1,130 @@
<#
.SYNOPSIS
Update the DistinguishedName Attribute for all Active Directory Users
.DESCRIPTION
Update the DistinguishedName Attribute for all Active Directory Users.
It will update the 'CN=' to match the SamAccountName
.EXAMPLE
PS C:\> .\Convert-ADDistinguishedNameForAllUser.ps1
.NOTES
MIND THE GAP:
This will change the DistinguishedName and this might break things
It will only update/change the DistinguishedName if the 'CN=' does NOT match the SamAccountName
I created this to bulk migrate older users, they had german umlauts and other crappy character in the DistinguishedName
.LINK
https://github.com/jhochwald/PowerShell-collection/
.LINK
Get-ADUser
.LINK
Rename-ADObject
#>
[CmdletBinding(ConfirmImpact = 'Medium',
SupportsShouldProcess)]
param ()
if ($pscmdlet.ShouldProcess('All Users', 'Set'))
{
try
{
$AllUsers = (Get-ADUser -Filter * -Properties SamAccountName, UserPrincipalName, DistinguishedName -ErrorAction Stop | Select-Object -Property SamAccountName, UserPrincipalName, DistinguishedName | Where-Object -FilterScript {
($_.UserPrincipalName) -and ($_.SamAccountName)
})
}
catch
{
# get error record
[Management.Automation.ErrorRecord]$e = $_
# retrieve information about runtime error
$info = [PSCustomObject]@{
Exception = $e.Exception.Message
Reason = $e.CategoryInfo.Reason
Target = $e.CategoryInfo.TargetName
Script = $e.InvocationInfo.ScriptName
Line = $e.InvocationInfo.ScriptLineNumber
Column = $e.InvocationInfo.OffsetInLine
}
$info | Out-String | Write-Verbose
Write-Error -Message $e.Exception.Message -ErrorAction Stop
break
}
try
{
foreach ($User in $AllUsers)
{
$OldRDN = (($User | Select-Object -Property @{
l = 'OldRDN'
e = {
$_.DistinguishedName.split(',')[0].split('=')[1]
}
}) | Select-Object -ExpandProperty OldRDN)
if ($OldRDN -ne ($User.SamAccountName))
{
# Mind the Gap: This will change the DistinguishedName and this might break things
$null = (Rename-ADObject -Identity $User.DistinguishedName -NewName $User.SamAccountName -Confirm:$false -ErrorAction Stop)
}
}
}
catch
{
# get error record
[Management.Automation.ErrorRecord]$e = $_
# retrieve information about runtime error
$info = [PSCustomObject]@{
Exception = $e.Exception.Message
Reason = $e.CategoryInfo.Reason
Target = $e.CategoryInfo.TargetName
Script = $e.InvocationInfo.ScriptName
Line = $e.InvocationInfo.ScriptLineNumber
Column = $e.InvocationInfo.OffsetInLine
}
$info | Out-String | Write-Verbose
Write-Warning -Message $e.Exception.Message -ErrorAction Continue -WarningAction Continue
}
}
#region LICENSE
<#
BSD 3-Clause License
Copyright (c) 2021, enabling Technology
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#>
#endregion LICENSE
#region DISCLAIMER
<#
DISCLAIMER:
- Use at your own risk, etc.
- This is open-source software, if you find an issue try to fix it yourself. There is no support and/or warranty in any kind
- This is a third-party Software
- The developer of this Software is NOT sponsored by or affiliated with Microsoft Corp (MSFT) or any of its subsidiaries in any way
- The Software is not supported by Microsoft Corp (MSFT)
- By using the Software, you agree to the License, Terms, and any Conditions declared and described above
- If you disagree with any of the terms, and any conditions declared: Just delete it and build your own solution
#>
#endregion DISCLAIMER

View File

@@ -0,0 +1,399 @@
function Copy-ADGroupUserMembership
{
<#
.SYNOPSIS
Copy the membership of a given group to another group in Active Directory
.DESCRIPTION
Copy the membership of a given group to another group in Active Directory.
By default only the members of the Source Group will be copied to the Target Group.
If the Parameter FULL is used, the members of the Target Group that are not a member of the Source Group will be removed.
If the Parameter SYNC is used, the Membership is synced between both groups. If a User is Member of the Target Group only, this membership will be copied to the Source as well.
.PARAMETER SourceGroup
Source-Group Object.
Specifies an Active Directory group object by providing one of the following values. The identifier in
parentheses is the LDAP display name for the attribute.
Distinguished Name
Example: CN=saradavisreports,OU=europe,CN=users,DC=corp,DC=contoso,DC=com
GUID (objectGUID)
Example: 599c3d2e-f72d-4d20-8a88-030d99495f20
Security Identifier (objectSid)
Example: S-1-5-21-3165297888-301567370-576410423-1103
Security Accounts Manager (SAM) Account Name (sAMAccountName)
Example: saradavisreports
The cmdlet searches the default naming context or partition to find the object. If two or more objects are
found, the cmdlet returns a non-terminating error.
This parameter can also get this object through the pipeline or you can set this parameter to an object
instance.
.PARAMETER TargetGroup
Target-Group Object.
Specifies an Active Directory group object by providing one of the following values. The identifier in
parentheses is the LDAP display name for the attribute.
Distinguished Name
Example: CN=saradavisreports,OU=europe,CN=users,DC=corp,DC=contoso,DC=com
GUID (objectGUID)
Example: 599c3d2e-f72d-4d20-8a88-030d99495f20
Security Identifier (objectSid)
Example: S-1-5-21-3165297888-301567370-576410423-1103
Security Accounts Manager (SAM) Account Name (sAMAccountName)
Example: saradavisreports
The cmdlet searches the default naming context or partition to find the object. If two or more objects are
found, the cmdlet returns a non-terminating error.
This parameter can also get this object through the pipeline or you can set this parameter to an object
instance.
.PARAMETER full
Remove all memberships from the Targewt that does NOT exist in the the Source.
.PARAMETER sync
Synchronies the group membership between Source-Group and Target-Group.
Even if a user is a member of the Target-Group only, it will be copied to the Source-Group as well.
.EXAMPLE
PS C:\> Copy-ADGroupUserMembership -SourceGroup 'Sales' -TargetGroup 'Salesforce'
Copy the membership of the Group 'Sales' to 'Salesforce'
.EXAMPLE
PS C:\> Copy-ADGroupUserMembership -SourceGroup 'Sales' -TargetGroup 'Salesforce' -sync
Copy the membership of the Group 'Sales' to 'Salesforce' and the other way around.
All Memberships of 'Salesforce' that does NOT exist in 'Sales' will be created in 'Sales' as well.
.EXAMPLE
PS C:\> Copy-ADGroupUserMembership -SourceGroup 'Sales' -TargetGroup 'Salesforce' -full
Copy the membership of the Group 'Sales' to 'Salesforce'.
All Memberships of 'Salesforce' that does NOT exist in 'Sales' will be removed.
.NOTES
Initial AIT version of the function
.LINK
https://github.com/jhochwald/PowerShell-collection/
.LINK
Get-ADGroupMember
.LINK
Remove-ADGroupMember
.LINK
Add-ADGroupMember
.LINK
Copy-ADUserGroupMemberships
#>
[CmdletBinding(DefaultParameterSetName = 'default',
ConfirmImpact = 'Low',
SupportsShouldProcess)]
param
(
[Parameter(Mandatory,
ValueFromPipeline,
ValueFromPipelineByPropertyName,
Position = 0,
HelpMessage = 'Source-Group Object.')]
[ValidateNotNullOrEmpty()]
[Alias('Source')]
[string]
$SourceGroup,
[Parameter(Mandatory,
ValueFromPipeline,
ValueFromPipelineByPropertyName,
Position = 1,
HelpMessage = 'Target-Group Object.')]
[ValidateNotNullOrEmpty()]
[Alias('Target')]
[string]
$TargetGroup,
[Parameter(ParameterSetName = 'full',
ValueFromPipeline,
ValueFromPipelineByPropertyName,
Position = 2)]
[Alias('RemoveTargetOnlyMembers')]
[switch]
$full = $null,
[Parameter(ParameterSetName = 'sync',
ValueFromPipeline,
ValueFromPipelineByPropertyName,
Position = 2)]
[Alias('MakeFullSync')]
[switch]
$sync = $null
)
begin
{
if ($pscmdlet.ShouldProcess('Groups', 'Get information from Active Directory'))
{
try
{
$SourceMembers = (Get-ADGroupMember -Identity $SourceGroup -ErrorAction Stop | Select-Object -ExpandProperty distinguishedName | Sort-Object)
$TargetMembers = (Get-ADGroupMember -Identity $TargetGroup -ErrorAction Stop | Select-Object -ExpandProperty distinguishedName | Sort-Object)
# Check if we have any diferences
if (($SourceMembers) -and ($TargetMembers))
{
# Yep, there are differences
$Differences = (Compare-Object -ReferenceObject $SourceMembers -DifferenceObject $TargetMembers)
}
elseif (($SourceMembers) -and (-not($TargetMembers)))
{
# Target has no members
$Differences = 'SourceOnly'
}
elseif (-not($SourceMembers))
{
# Source has no members
Write-Error -Message ('{0} has no members!' -f $SourceGroup) -ErrorAction Stop
}
else
{
# Nope, there are no differences
$Differences = $null
}
}
catch
{
# get error record
[Management.Automation.ErrorRecord]$e = $_
# retrieve information about runtime error
$info = [PSCustomObject]@{
Exception = $e.Exception.Message
Reason = $e.CategoryInfo.Reason
Target = $e.CategoryInfo.TargetName
Script = $e.InvocationInfo.ScriptName
Line = $e.InvocationInfo.ScriptLineNumber
Column = $e.InvocationInfo.OffsetInLine
}
$info | Out-String | Write-Verbose
Write-Error -Message $e.Exception.Message -ErrorAction Stop
break
}
}
}
process
{
switch ($pscmdlet.ParameterSetName)
{
'full'
{
if ($pscmdlet.ShouldProcess($TargetGroup, 'Set'))
{
if ($Differences)
{
Write-Verbose -Message 'Remove Target-User from all groups where the Source-User is not a member of.'
$TargetOnlyMembers = ($Differences | Where-Object -Property SideIndicator -EQ -Value '=>')
if ($TargetOnlyMembers)
{
try
{
foreach ($TargetOnlyMember in $TargetOnlyMembers.InputObject)
{
Write-Verbose -Message ('Process: {0}' -f $TargetOnlyMember)
$paramRemoveADGroupMember = @{
Identity = $TargetGroup
Members = $TargetOnlyMember
ErrorAction = 'Stop'
Confirm = $false
}
$null = (Remove-ADGroupMember @paramRemoveADGroupMember -Verbose)
}
}
catch
{
# get error record
[Management.Automation.ErrorRecord]$e = $_
# retrieve information about runtime error
$info = [PSCustomObject]@{
Exception = $e.Exception.Message
Reason = $e.CategoryInfo.Reason
Target = $e.CategoryInfo.TargetName
Script = $e.InvocationInfo.ScriptName
Line = $e.InvocationInfo.ScriptLineNumber
Column = $e.InvocationInfo.OffsetInLine
}
$info | Out-String | Write-Verbose
Write-Warning -Message $e.Exception.Message -ErrorAction Continue -WarningAction Continue
}
}
else
{
Write-Verbose -Message 'No group difference found where the Target-User is a member and Source-User is not.'
}
}
}
}
'sync'
{
if ($pscmdlet.ShouldProcess($SourceGroup, 'Set'))
{
if ($Differences)
{
Write-Verbose -Message 'Make the Source-user a Member of all Groups only the Target-User is a member of.'
$TargetOnlyMembers = ($Differences | Where-Object -Property SideIndicator -EQ -Value '=>')
if ($TargetOnlyMembers)
{
Write-Verbose -Message ('Process: {0}' -f $TargetOnlyMembers)
try
{
$paramAddADGroupMember = @{
Identity = $SourceGroup
Members = $TargetOnlyMembers.InputObject
ErrorAction = 'Stop'
Confirm = $false
}
$null = (Add-ADGroupMember @paramAddADGroupMember)
}
catch
{
# get error record
[Management.Automation.ErrorRecord]$e = $_
# retrieve information about runtime error
$info = [PSCustomObject]@{
Exception = $e.Exception.Message
Reason = $e.CategoryInfo.Reason
Target = $e.CategoryInfo.TargetName
Script = $e.InvocationInfo.ScriptName
Line = $e.InvocationInfo.ScriptLineNumber
Column = $e.InvocationInfo.OffsetInLine
}
$info | Out-String | Write-Verbose
Write-Warning -Message $e.Exception.Message -ErrorAction Continue -WarningAction Continue
}
}
else
{
Write-Verbose -Message 'No group difference found where the Target-User is a member and Source-User is not.'
}
}
}
}
'default'
{
# Do nothing special
}
}
if ($pscmdlet.ShouldProcess($TargetGroup, 'Set'))
{
if ($Differences)
{
try
{
Write-Verbose -Message 'Process all Source-Group only members.'
$paramAddADGroupMember = @{
Identity = $TargetGroup
ErrorAction = 'Stop'
Confirm = $false
}
if ($Differences -eq 'SourceOnly')
{
# Target has no members
$paramAddADGroupMember.Members = $SourceMembers
}
else
{
$paramAddADGroupMember.Members = ($Differences | Where-Object -Property SideIndicator -EQ -Value '<=' | Select-Object -ExpandProperty InputObject)
}
$null = (Add-ADGroupMember @paramAddADGroupMember)
}
catch
{
# get error record
[Management.Automation.ErrorRecord]$e = $_
# retrieve information about runtime error
$info = [PSCustomObject]@{
Exception = $e.Exception.Message
Reason = $e.CategoryInfo.Reason
Target = $e.CategoryInfo.TargetName
Script = $e.InvocationInfo.ScriptName
Line = $e.InvocationInfo.ScriptLineNumber
Column = $e.InvocationInfo.OffsetInLine
}
$info | Out-String | Write-Verbose
Write-Warning -Message $e.Exception.Message -ErrorAction Continue -WarningAction Continue
}
}
}
}
}
#region LICENSE
<#
BSD 3-Clause License
Copyright (c) 2021, enabling Technology
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#>
#endregion LICENSE
#region DISCLAIMER
<#
DISCLAIMER:
- Use at your own risk, etc.
- This is open-source software, if you find an issue try to fix it yourself. There is no support and/or warranty in any kind
- This is a third-party Software
- The developer of this Software is NOT sponsored by or affiliated with Microsoft Corp (MSFT) or any of its subsidiaries in any way
- The Software is not supported by Microsoft Corp (MSFT)
- By using the Software, you agree to the License, Terms, and any Conditions declared and described above
- If you disagree with any of the terms, and any conditions declared: Just delete it and build your own solution
#>
#endregion DISCLAIMER

View File

@@ -0,0 +1,385 @@
function Copy-ADUserGroupMembership
{
<#
.SYNOPSIS
Copy group memberships from a given Source-User to a Target-User in Active Directory
.DESCRIPTION
Copy group memberships from a given Source-User to a Target-User in Active Directory.
The function can also remove the Target-User from all groups where the Source-User is not a member off (optional) or make the Source-User a member of all groups where only the Target-User is a member of.
.PARAMETER SourceUser
Source-User Object.
Specifies an Active Directory user object by providing one of the following property values.
The identifier in parentheses is the LDAP display name for the attribute.
Distinguished Name
Example: CN=SaraDavis,CN=Europe,CN=Users,DC=corp,DC=contoso,DC=com
GUID (objectGUID)
Example: 599c3d2e-f72d-4d20-8a88-030d99495f20
Security Identifier (objectSid)
Example: S-1-5-21-3165297888-301567370-576410423-1103
SAM account name (sAMAccountName)
Example: saradavis
.PARAMETER TargetUser
Target-User Object.
Specifies an Active Directory user object by providing one of the following property values.
The identifier in parentheses is the LDAP display name for the attribute.
Distinguished Name
Example: CN=SaraDavis,CN=Europe,CN=Users,DC=corp,DC=contoso,DC=com
GUID (objectGUID)
Example: 599c3d2e-f72d-4d20-8a88-030d99495f20
Security Identifier (objectSid)
Example: S-1-5-21-3165297888-301567370-576410423-1103
SAM account name (sAMAccountName)
Example: saradavis
.PARAMETER full
Remove the Target User from all groups where the Source-User is not a member of.
.PARAMETER sync
Make the Source-User a member of all Groups where only the Target-User is a member of.
.EXAMPLE
PS C:\> Copy-ADUserGroupMembership -SourceUser 'johndoe' -TargetUser 'janedoe'
Make janedoe a member of all groups where johndoe is a member of. Existing group memberships of janedoe will NOT be removed.
.EXAMPLE
PS C:\> Copy-ADUserGroupMembership -SourceUser 'johndoe' -TargetUser 'janedoe' -full
Make janedoe a member of all groups where johndoe is a member of. Existing group memberships of janedoe WILL be removed.
.EXAMPLE
PS C:\> Copy-ADUserGroupMembership -SourceUser 'johndoe' -TargetUser 'janedoe' -sync
Make janedoe a member of all groups where johndoe is a member of. Existing group memberships of janedoe WILL be applied to johndoe.
Lets call this a reverse Full Sync :)
.NOTES
Initial AIT version of the function
.LINK
https://github.com/jhochwald/PowerShell-collection/
.LINK
Get-ADUser
.LINK
Remove-ADGroupMember
.LINK
Add-ADGroupMember
.LINK
Copy-ADGroupUserMemberships
#>
[CmdletBinding(DefaultParameterSetName = 'default',
SupportsShouldProcess)]
param
(
[Parameter(Mandatory,
ValueFromPipeline,
ValueFromPipelineByPropertyName,
Position = 0,
HelpMessage = 'Source-User Object.')]
[ValidateNotNullOrEmpty()]
[Alias('Source')]
[string]
$SourceUser,
[Parameter(Mandatory,
ValueFromPipeline,
ValueFromPipelineByPropertyName,
Position = 1,
HelpMessage = 'Target-User Object.')]
[ValidateNotNullOrEmpty()]
[Alias('Target')]
[string]
$TargetUser,
[Parameter(ParameterSetName = 'full',
ValueFromPipeline,
ValueFromPipelineByPropertyName,
Position = 2)]
[Alias('RemoveTargetOnlyGroups')]
[switch]
$full = $null,
[Parameter(ParameterSetName = 'sync',
ValueFromPipeline,
ValueFromPipelineByPropertyName,
Position = 2)]
[Alias('MakeFullSync')]
[switch]
$sync = $null
)
begin
{
if ($pscmdlet.ShouldProcess('User', 'Get information from Active Directory'))
{
try
{
# Get the Target-User
$TargetUserObject = (Get-ADUser -Identity $TargetUser -Properties memberOf -ErrorAction Stop)
# Get the Source-User
$SourceUserObject = (Get-ADUser -Identity $SourceUser -Properties memberOf -ErrorAction Stop)
# Sort and save the information we collected above
$SourceUserMembership = ($SourceUserObject.MemberOf | Sort-Object)
$TargetUserMembership = ($TargetUserObject.MemberOf | Sort-Object)
# Check if we have any diferences
if (($SourceUserMembership) -and ($TargetUserMembership))
{
# Yep, there are differences
$Differences = (Compare-Object -ReferenceObject $SourceUserMembership -DifferenceObject $TargetUserMembership)
}
else
{
# Nope, there are no differences
$Differences = $null
}
}
catch
{
# get error record
[Management.Automation.ErrorRecord]$e = $_
# retrieve information about runtime error
$info = [PSCustomObject]@{
Exception = $e.Exception.Message
Reason = $e.CategoryInfo.Reason
Target = $e.CategoryInfo.TargetName
Script = $e.InvocationInfo.ScriptName
Line = $e.InvocationInfo.ScriptLineNumber
Column = $e.InvocationInfo.OffsetInLine
}
$info | Out-String | Write-Verbose
Write-Error -Message $e.Exception.Message -ErrorAction Stop
break
}
}
}
process
{
switch ($pscmdlet.ParameterSetName)
{
'full'
{
if ($pscmdlet.ShouldProcess($SourceUser, 'Set'))
{
if ($Differences)
{
Write-Verbose -Message 'Remove Target-User from all groups where the Source-User is not a member of.'
$TargetOnlyGroups = ($Differences | Where-Object -Property SideIndicator -EQ -Value '=>')
if ($TargetOnlyGroups)
{
foreach ($TargetOnlyGroup in $TargetOnlyGroups.InputObject)
{
Write-Verbose -Message ('Process: {0}' -f $TargetOnlyGroup)
try
{
$paramRemoveADGroupMember = @{
Identity = $TargetOnlyGroup
Members = $TargetUser
ErrorAction = 'Stop'
Confirm = $false
}
$null = (Remove-ADGroupMember @paramRemoveADGroupMember)
}
catch
{
# get error record
[Management.Automation.ErrorRecord]$e = $_
# retrieve information about runtime error
$info = [PSCustomObject]@{
Exception = $e.Exception.Message
Reason = $e.CategoryInfo.Reason
Target = $e.CategoryInfo.TargetName
Script = $e.InvocationInfo.ScriptName
Line = $e.InvocationInfo.ScriptLineNumber
Column = $e.InvocationInfo.OffsetInLine
}
$info | Out-String | Write-Verbose
Write-Warning -Message $e.Exception.Message -ErrorAction Continue -WarningAction Continue
}
}
}
else
{
Write-Verbose -Message 'No group difference fround where the Target-User is a member and Source-User is not.'
}
}
}
}
'sync'
{
if ($pscmdlet.ShouldProcess($SourceUser, 'Set'))
{
if ($Differences)
{
Write-Verbose -Message 'Make the Source-user a Member of all Groups only the Target-User is a member of.'
$TargetOnlyGroups = ($Differences | Where-Object -Property SideIndicator -EQ -Value '=>')
if ($TargetOnlyGroups)
{
foreach ($TargetOnlyGroup in $TargetOnlyGroups.InputObject)
{
Write-Verbose -Message ('Process: {0}' -f $TargetOnlyGroup)
try
{
$paramAddADGroupMember = @{
Identity = $TargetOnlyGroup
Members = $SourceUser
ErrorAction = 'Stop'
Confirm = $false
}
$null = (Add-ADGroupMember @paramAddADGroupMember)
}
catch
{
# get error record
[Management.Automation.ErrorRecord]$e = $_
# retrieve information about runtime error
$info = [PSCustomObject]@{
Exception = $e.Exception.Message
Reason = $e.CategoryInfo.Reason
Target = $e.CategoryInfo.TargetName
Script = $e.InvocationInfo.ScriptName
Line = $e.InvocationInfo.ScriptLineNumber
Column = $e.InvocationInfo.OffsetInLine
}
$info | Out-String | Write-Verbose
Write-Warning -Message $e.Exception.Message -ErrorAction Continue -WarningAction Continue
}
}
}
else
{
Write-Verbose -Message 'No group difference fround where the Target-User is a member and Source-User is not.'
}
}
}
}
'default'
{
# Do nothing special
}
}
if ($pscmdlet.ShouldProcess($TargetUser, 'Set'))
{
if ($Differences)
{
$SourceOnlyGroups = ($Differences | Where-Object -Property SideIndicator -EQ -Value '<=')
if ($SourceOnlyGroups)
{
Write-Verbose -Message 'Process all Groups where only the Source-user is a member of.'
foreach ($SourceOnlyGroup in $SourceOnlyGroups.InputObject)
{
Write-Verbose -Message ('Process: {0}' -f $SourceOnlyGroup)
try
{
$paramAddADGroupMember = @{
Identity = $SourceOnlyGroup
Members = $TargetUser
ErrorAction = 'Stop'
Confirm = $false
}
$null = (Add-ADGroupMember @paramAddADGroupMember)
}
catch
{
# get error record
[Management.Automation.ErrorRecord]$e = $_
# retrieve information about runtime error
$info = [PSCustomObject]@{
Exception = $e.Exception.Message
Reason = $e.CategoryInfo.Reason
Target = $e.CategoryInfo.TargetName
Script = $e.InvocationInfo.ScriptName
Line = $e.InvocationInfo.ScriptLineNumber
Column = $e.InvocationInfo.OffsetInLine
}
$info | Out-String | Write-Verbose
Write-Warning -Message $e.Exception.Message -ErrorAction Continue -WarningAction Continue
}
}
}
}
else
{
Write-Warning -Message 'No group difference fround where the Source-User is a member and Source-User is not.' -WarningAction Continue
}
}
}
}
#region LICENSE
<#
BSD 3-Clause License
Copyright (c) 2021, enabling Technology
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#>
#endregion LICENSE
#region DISCLAIMER
<#
DISCLAIMER:
- Use at your own risk, etc.
- This is open-source software, if you find an issue try to fix it yourself. There is no support and/or warranty in any kind
- This is a third-party Software
- The developer of this Software is NOT sponsored by or affiliated with Microsoft Corp (MSFT) or any of its subsidiaries in any way
- The Software is not supported by Microsoft Corp (MSFT)
- By using the Software, you agree to the License, Terms, and any Conditions declared and described above
- If you disagree with any of the terms, and any conditions declared: Just delete it and build your own solution
#>
#endregion DISCLAIMER

View File

@@ -0,0 +1,224 @@
function Copy-ADUserGroupMembershipSimple
{
<#
.SYNOPSIS
Copy group memberships from a given Source User to a Target User(s) in Active Directory
.DESCRIPTION
Copy group memberships from a given Source User to a Target User(s) in Active Directory.
Simple Version of Copy-ADUserGroupMemberships
.PARAMETER SourceUser
Source-User Object.
Specifies an Active Directory group object by providing one of the following values.
The identifier in parentheses is the LDAP display name for the attribute.
Distinguished Name
Example: CN=johndoe,OU=europe,CN=users,DC=corp,DC=contoso,DC=com
GUID (objectGUID)
Example: 599c3d2e-f72d-4d20-8a88-030d99495f20
Security Identifier (objectSid)
Example: S-1-5-21-3165297888-301567370-576410423-1103
Security Accounts Manager (SAM) Account Name (sAMAccountName)
Example: johndoe
.PARAMETER TargetUser
Target-User Object.
Specifies an Active Directory group object by providing one of the following values.
The identifier in parentheses is the LDAP display name for the attribute.
Distinguished Name
Example: CN=janedoe,OU=europe,CN=users,DC=corp,DC=contoso,DC=com
GUID (objectGUID)
Example: 599c3d2e-f72d-4d20-8a88-030d99495f20
Security Identifier (objectSid)
Example: S-1-5-21-3165297888-301567370-576410423-1103
Security Accounts Manager (SAM) Account Name (sAMAccountName)
Example: janedoe
.PARAMETER PassThru
Use the -PassThru parameter with the previous command to receive feedback about what groups the Target is being added as a member of.
.EXAMPLE
PS C:\> Copy-ADUserGroupMembershipSimple -SourceUser 'SourceUser' -TargetUser 'TargetUser'
Copy group memberships from SourceUser to TargetUser
.EXAMPLE
PS C:\> Copy-ADUserGroupMembershipSimple -SourceUser 'SourceUser' -TargetUser 'TargetUser1', 'TargetUser2'
Copy group memberships from SourceUser to TargetUser1 and TargetUser2
.EXAMPLE
PS C:\> Copy-ADUserGroupMembershipSimple -SourceUser 'SourceUser' -TargetUser 'TargetUser' -PassThru
Use the -PassThru parameter with the previous command to receive feedback about what groups the Target is being added as a member of.
.NOTES
Releasenotes:
1.0.0 2019-07-09: Initial Release
THIS CODE IS MADE AVAILABLE AS IS, WITHOUT WARRANTY OF ANY KIND. THE ENTIRE RISK OF THE USE OR THE RESULTS FROM THE USE OF THIS CODE REMAINS WITH THE USER.
.LINK
Copy-ADUserGroupMemberships
.LINK
https://github.com/jhochwald/PowerShell-collection/
.LINK
Get-ADUser
.LINK
Add-ADGroupMember
#>
[CmdletBinding(ConfirmImpact = 'Low',
SupportsShouldProcess = $true)]
param
(
[Parameter(Mandatory = $true,
ValueFromPipeline = $true,
ValueFromPipelineByPropertyName = $true,
Position = 0,
HelpMessage = 'Source-User Object.')]
[ValidateNotNullOrEmpty()]
[Alias('Source')]
[string]
$SourceUser,
[Parameter(Mandatory = $true,
ValueFromPipeline = $true,
ValueFromPipelineByPropertyName = $true,
Position = 1,
HelpMessage = 'Target-User Object.')]
[ValidateNotNullOrEmpty()]
[Alias('Target')]
[string[]]
$TargetUser,
[Parameter(ValueFromPipeline = $true,
ValueFromPipelineByPropertyName = $true,
Position = 2)]
[switch]
$PassThru = $null
)
process
{
if ($pscmdlet.ShouldProcess($TargetUser, 'Modify/add Group Membership'))
{
try
{
$paramGetADUser = @{
Identity = $SourceUser
Properties = 'memberof'
Verbose = $(if ($pscmdlet.MyInvocation.BoundParameters['Verbose'].IsPresent)
{
$true
}
else
{
$false
}
)
}
$paramAddADGroupMember = @{
Members = $TargetUser
Verbose = $(if ($pscmdlet.MyInvocation.BoundParameters['Verbose'].IsPresent)
{
$true
}
else
{
# Workaround: If not present it is empty not false
$false
}
)
PassThru = $(if ($pscmdlet.MyInvocation.BoundParameters['PassThru'].IsPresent)
{
$true
}
else
{
# Workaround: If not present it is empty not false
$false
}
)
}
if (($pscmdlet.MyInvocation.BoundParameters['PassThru'].IsPresent))
{
# Show the output / PassThru in a nice format
((Get-ADUser @paramGetADUser) | Select-Object -ExpandProperty memberof | Add-ADGroupMember @paramAddADGroupMember | Select-Object -ExpandProperty SamAccountName)
}
else
{
# Do not show any output / Verbose will be shown
$null = ((Get-ADUser @paramGetADUser) | Select-Object -ExpandProperty memberof | Add-ADGroupMember @paramAddADGroupMember)
}
}
catch
{
#region ErrorHandler
# get error record
[Management.Automation.ErrorRecord]$e = $_
# retrieve information about runtime error
$info = [PSCustomObject]@{
Exception = $e.Exception.Message
Reason = $e.CategoryInfo.Reason
Target = $e.CategoryInfo.TargetName
Script = $e.InvocationInfo.ScriptName
Line = $e.InvocationInfo.ScriptLineNumber
Column = $e.InvocationInfo.OffsetInLine
}
$info | Out-String | Write-Verbose
Write-Error -Message ($info.Exception) -ErrorAction Stop
# Only here to catch a global ErrorAction overwrite
break
#endregion ErrorHandler
}
}
}
}
#region LICENSE
<#
BSD 3-Clause License
Copyright (c) 2021, enabling Technology
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#>
#endregion LICENSE
#region DISCLAIMER
<#
DISCLAIMER:
- Use at your own risk, etc.
- This is open-source software, if you find an issue try to fix it yourself. There is no support and/or warranty in any kind
- This is a third-party Software
- The developer of this Software is NOT sponsored by or affiliated with Microsoft Corp (MSFT) or any of its subsidiaries in any way
- The Software is not supported by Microsoft Corp (MSFT)
- By using the Software, you agree to the License, Terms, and any Conditions declared and described above
- If you disagree with any of the terms, and any conditions declared: Just delete it and build your own solution
#>
#endregion DISCLAIMER

View File

@@ -0,0 +1,169 @@
function Find-enADDuplicateServicePrincipalName
{
<#
.SYNOPSIS
Find all duplicate Service Principal Names (SPNs)
.DESCRIPTION
Find all duplicate Service Principal Names (SPNs) in the Active Directory
.INPUTS
NONE
.OUTPUTS
Boolean
.EXAMPLE
PS C:\> Find-enADDuplicateServicePrincipalName
.NOTES
Releasenotes:
1.0.1 2019-07-26 Refactored, License change to BSD 3-Clause
1.0.0 2019-01-01 Initial Version
THIS CODE IS MADE AVAILABLE AS IS, WITHOUT WARRANTY OF ANY KIND. THE ENTIRE RISK OF THE USE OR THE RESULTS FROM THE USE OF THIS CODE REMAINS WITH THE USER.
Dependencies:
Active Directory PowerShell Module
.LINK
https://www.enatec.io
.LINK
Get-ADObject
#>
[CmdletBinding(ConfirmImpact = 'None')]
[OutputType([bool])]
param ()
begin
{
# Create a new Object
$AllObject = @()
}
process
{
try
{
# We use Get-ADObject because this seems to be fast enough
$paramGetADObject = @{
Filter = "(objectClass -eq 'user') -or (objectClass -eq 'computer') -and (servicePrincipalName -like '*')"
Properties = 'SamAccountName', 'servicePrincipalName'
ErrorAction = 'Stop'
WarningAction = 'Continue'
}
$AllServicePrincipalNames = (Get-ADObject @paramGetADObject)
# Loop over the List we got from Get-ADObject
foreach ($SPNObject in $AllServicePrincipalNames)
{
$SamAccountName = $SPNObject.SamAccountName
$ServicePrincipalNames = $SPNObject.ServicePrincipalName
foreach ($ServicePrincipalName in $ServicePrincipalNames)
{
if ($AllObject.ServicePrincipalName -like $ServicePrincipalName)
{
$MatchedSPNs = ($AllObject.ServicePrincipalName -like $ServicePrincipalName)
# Loop over the matching list og SPNs
foreach ($MatchSPN in $MatchedSPNs)
{
$MatchSamAccountName = $MatchSPN.SamAccountName
# Ding. ding, we have a winner
if ($MatchSamAccountName -ne $SamAccountName)
{
$paramWriteWarning = @{
Message = ('Duplicated SPN has been found for {0}!!!' -f $ServicePrincipalName)
ErrorAction = 'Continue'
WarningAction = 'Continue'
}
Write-Warning @paramWriteWarning
}
}
}
else
{
# Create a new Object
$SingleObject = (New-Object -TypeName PSObject -Property @{
SamAccountName = $SamAccountName
ServicePrincipalName = $ServicePrincipalName
})
# Add the Values to the List
$AllObject += $SingleObject
# Cleanup
$SingleObject = $null
}
}
}
}
catch
{
#region ErrorHandler
# get error record
[Management.Automation.ErrorRecord]$e = $_
# retrieve information about runtime error
$info = [PSCustomObject]@{
Exception = $e.Exception.Message
Reason = $e.CategoryInfo.Reason
Target = $e.CategoryInfo.TargetName
Script = $e.InvocationInfo.ScriptName
Line = $e.InvocationInfo.ScriptLineNumber
Column = $e.InvocationInfo.OffsetInLine
}
$info | Out-String | Write-Verbose
Write-Error -Message ($info.Exception) -ErrorAction Stop
# Only here to catch a global ErrorAction overwrite
break
#endregion ErrorHandler
}
}
end
{
# Dump all SPNs, if verbose
$AllObject | Out-String | Write-Verbose
# Cleanup
$AllObject = $null
}
}
#region LICENSE
<#
BSD 3-Clause License
Copyright (c) 2021, enabling Technology
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#>
#endregion LICENSE
#region DISCLAIMER
<#
DISCLAIMER:
- Use at your own risk, etc.
- This is open-source software, if you find an issue try to fix it yourself. There is no support and/or warranty in any kind
- This is a third-party Software
- The developer of this Software is NOT sponsored by or affiliated with Microsoft Corp (MSFT) or any of its subsidiaries in any way
- The Software is not supported by Microsoft Corp (MSFT)
- By using the Software, you agree to the License, Terms, and any Conditions declared and described above
- If you disagree with any of the terms, and any conditions declared: Just delete it and build your own solution
#>
#endregion DISCLAIMER

View File

@@ -0,0 +1,203 @@
function Get-ADUserLockouts
{
<#
.SYNOPSIS
Tracking down account lockout sources with PowerShell
.DESCRIPTION
Tracking down account lockout sources with PowerShell
.PARAMETER Identity
Just scan for a single User?
.PARAMETER StartTime
Start-point
.PARAMETER EndTime
Endpoint
.EXAMPLE
PS C:\> Get-ADUserLockout
Tracking down account lockout sources for all users for the last 7 days
.EXAMPLE
Get-ADUser -Filter {Department -eq 'Development'} | Get-ADUserLockout
Tracking down account lockout sources for all users in the Development Department for the last 7 days
.EXAMPLE
Get-ADUserLockout -StartTime (Get-Date).AddDays(-2) -EndTime (Get-Date).AddDays(-1)
Tracking down account lockout sources for all users for the last day
.NOTES
Original by Anthony Howell (@ThePoShWolf) - MIT Licenses
Copyright (c) 2018 Anthony Howell
.LINK
https://theposhwolf.com/howtos/Get-ADUserLockouts/
.LINK
https://github.com/ThePoShWolf/Utilities/blob/master/ActiveDirectory/Get-ADUserLockouts.ps1
#>
[CmdletBinding(DefaultParameterSetName = 'All',
ConfirmImpact = 'None')]
[OutputType([pscustomobject])]
param
(
[Parameter(ParameterSetName = 'ByUser',
ValueFromPipeline)]
[string]
$Identity,
[Parameter(ValueFromPipeline,
ValueFromPipelineByPropertyName)]
[Alias('Start')]
[datetime]
$StartTime = (Get-Date).AddDays(-8),
[Parameter(ValueFromPipeline,
ValueFromPipelineByPropertyName)]
[Alias('End')]
[datetime]
$EndTime = (Get-Date).AddDays(-1)
)
begin
{
$filterHt = @{
LogName = 'Security'
ID = 4740
}
if ($PSBoundParameters.ContainsKey('StartTime'))
{
$filterHt['StartTime'] = $StartTime
}
if ($PSBoundParameters.ContainsKey('EndTime'))
{
$filterHt['EndTime'] = $EndTime
}
try
{
$PDCEmulator = ((Get-ADDomain -ErrorAction Stop).PDCEmulator)
Write-Verbose -Message ('Use {0} to find the lockout events' -f $PDCEmulator)
# Query the event log just once instead of for each user if using the pipeline
$events = (Get-WinEvent -ComputerName $PDCEmulator -FilterHashtable $filterHt -ErrorAction Stop)
}
catch
{
# get error record
[Management.Automation.ErrorRecord]$e = $_
# retrieve information about runtime error
$info = [PSCustomObject]@{
Exception = $e.Exception.Message
Reason = $e.CategoryInfo.Reason
Target = $e.CategoryInfo.TargetName
Script = $e.InvocationInfo.ScriptName
Line = $e.InvocationInfo.ScriptLineNumber
Column = $e.InvocationInfo.OffsetInLine
}
# output information. Post-process collected info, and log info (optional)
$info | Out-String | Write-Verbose
Write-Error -Message $e.Exception.Message -ErrorAction Stop -Exception $e.Exception -TargetObject $e.CategoryInfo.TargetName
break
}
Write-Verbose -Message 'Found the following events:'
Write-Verbose -Message $events
}
process
{
if ($PSCmdlet.ParameterSetName -eq 'ByUser')
{
try
{
Write-Verbose -Message ('Querry AD Info for {0}' -f $Identity)
$user = (Get-ADUser -Identity $Identity -ErrorAction Stop)
Write-Verbose -Message ('Found the following AD Info for {0}:' -f $Identity)
Write-Verbose -Message $user
# Filter the events
$output = $events | Where-Object -FilterScript {
$_.Properties[0].Value -eq $user.SamAccountName
}
}
catch
{
# get error record
[Management.Automation.ErrorRecord]$e = $_
# retrieve information about runtime error
$info = [PSCustomObject]@{
Exception = $e.Exception.Message
Reason = $e.CategoryInfo.Reason
Target = $e.CategoryInfo.TargetName
Script = $e.InvocationInfo.ScriptName
Line = $e.InvocationInfo.ScriptLineNumber
Column = $e.InvocationInfo.OffsetInLine
}
# output information. Post-process collected info, and log info (optional)
$info | Out-String | Write-Verbose
Write-Error -Message $e.Exception.Message -ErrorAction Stop -Exception $e.Exception -TargetObject $e.CategoryInfo.TargetName
break
}
}
else
{
$output = $events
}
foreach ($event in $output)
{
[pscustomobject]@{
UserName = $event.Properties[0].Value
CallerComputer = $event.Properties[1].Value
TimeStamp = $event.TimeCreated
}
}
}
}
#region LICENSE
<#
BSD 3-Clause License
Copyright (c) 2021, enabling Technology
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#>
#endregion LICENSE
#region DISCLAIMER
<#
DISCLAIMER:
- Use at your own risk, etc.
- This is open-source software, if you find an issue try to fix it yourself. There is no support and/or warranty in any kind
- This is a third-party Software
- The developer of this Software is NOT sponsored by or affiliated with Microsoft Corp (MSFT) or any of its subsidiaries in any way
- The Software is not supported by Microsoft Corp (MSFT)
- By using the Software, you agree to the License, Terms, and any Conditions declared and described above
- If you disagree with any of the terms, and any conditions declared: Just delete it and build your own solution
#>
#endregion DISCLAIMER

View File

@@ -0,0 +1,196 @@
function Get-enADDNSServerInformation
{
<#
.SYNOPSIS
Retrieve information about the Active Directory Domain Name Servers
.DESCRIPTION
Retrieve information about the Active Directory Domain Name Servers
.PARAMETER Domain
A description of the Domain parameter.
.EXAMPLE
PS ~> Get-enADDNSServerInformation
Retrieve information about the Active Directory Domain Name Servers, use the current domain
.EXAMPLE
PS ~> Get-enADDNSServerInformation | Export-CSV -Path C:\scripts\PowerShell\Reports\DNS_Zones.csv -NoTypeInformation -Force -Confirm:$false
Retrieve information about the Active Directory Domain Name Servers, use the current domain and exports it to CSV
.EXAMPLE
PS ~> Get-enADDNSServerInformation | ConvertTo-Json -Depth 10 | Set-Content -Path C:\scripts\PowerShell\Reports\DNS_Zones.json -Force -Confirm:$false
Retrieve information about the Active Directory Domain Name Servers, use the current domain and exports it to a JSON File
.EXAMPLE
PS ~> Get-enADDNSServerInformation -Domain 'contoso.com'
Retrieve information about the Active Directory Domain Name Servers in the Domain contoso.com
.EXAMPLE
PS ~> Get-enADDNSServerInformation -Domain 'contoso.com', 'corp.contoso.net'
Retrieve information about the Active Directory Domain Name Servers in the Domain contoso.com and corp.contoso.net
.OUTPUTS
psobject
.INPUTS
String
.NOTES
TODO: Need refactoring: Object handler sucks
TODO: Find a non WMI based Solution for this
Version: 1.0.1
GUID: 4404141a-1731-4786-8bbf-ee6706765050
Author: Joerg Hochwald
Companyname: enabling Technology
Copyright: Copyright (c) 2ß18-2019, enabling Technology - All rights reserved.
License: https://opensource.org/licenses/BSD-3-Clause
Releasenotes:
1.0.1 2019-07-26 Refactored, License change to BSD 3-Clause
1.0.0 2019-01-01 Initial Version
THIS CODE IS MADE AVAILABLE AS IS, WITHOUT WARRANTY OF ANY KIND. THE ENTIRE RISK OF THE USE OR THE RESULTS FROM THE USE OF THIS CODE REMAINS WITH THE USER.
.LINK
https://www.enatec.io
.LINK
http://msdn.microsoft.com/en-us/library/windows/desktop/aa393295(v=vs.85).aspx
.LINK
Get-ADDomainController
.LINK
Get-WmiObject
#>
[CmdletBinding(ConfirmImpact = 'None')]
[OutputType([psobject])]
param
(
[Parameter(ValueFromPipeline,
ValueFromPipelineByPropertyName)]
[string[]]
$Domain = ([DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain().Name.ToString())
)
begin
{
$DNSReport = @()
}
process
{
foreach ($DomainEach in $Domain)
{
$AllDomainControllers = (Get-ADDomainController -Filter {
Site -like '*' -and Domain -eq $DomainEach
} | Select-Object -ExpandProperty Name)
foreach ($SingleDomainController in $AllDomainControllers)
{
# Prevent Null Pointer Exceptions
if ($SingleDomainController)
{
# TODO: Find a non WMI based Solution for this
$Forwarders = (Get-WmiObject -ComputerName $SingleDomainController -Namespace root\MicrosoftDNS -Class MicrosoftDNS_Server -ErrorAction SilentlyContinue)
# TODO: Find a non WMI based Solution for this
$NetworkInterface = (Get-WmiObject -ComputerName $SingleDomainController -Query 'Select * From Win32_NetworkAdapterConfiguration Where IPEnabled=TRUE' -ErrorAction SilentlyContinue)
$DNSReport += 1 | Select-Object -Property @{
name = 'DC'
expression = {
$SingleDomainController
}
}, @{
name = 'Domain'
expression = {
$DomainEach
}
}, @{
name = 'DNSHostName'
expression = {
$NetworkInterface.DNSHostName
}
}, @{
name = 'IPAddress'
expression = {
$NetworkInterface.IPAddress
}
}, @{
name = 'DNSServerAddresses'
expression = {
$Forwarders.ServerAddresses
}
}, @{
name = 'DNSServerSearchOrder'
expression = {
$NetworkInterface.DNSServerSearchOrder
}
}, @{
name = 'Forwarders'
expression = {
$Forwarders.Forwarders
}
}, @{
name = 'BootMethod'
expression = {
$Forwarders.BootMethod
}
}, @{
name = 'ScavengingInterval'
expression = {
$Forwarders.ScavengingInterval
}
}
}
}
}
}
end
{
$DNSReport
}
}
#region LICENSE
<#
BSD 3-Clause License
Copyright (c) 2021, enabling Technology
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#>
#endregion LICENSE
#region DISCLAIMER
<#
DISCLAIMER:
- Use at your own risk, etc.
- This is open-source software, if you find an issue try to fix it yourself. There is no support and/or warranty in any kind
- This is a third-party Software
- The developer of this Software is NOT sponsored by or affiliated with Microsoft Corp (MSFT) or any of its subsidiaries in any way
- The Software is not supported by Microsoft Corp (MSFT)
- By using the Software, you agree to the License, Terms, and any Conditions declared and described above
- If you disagree with any of the terms, and any conditions declared: Just delete it and build your own solution
#>
#endregion DISCLAIMER

View File

@@ -0,0 +1,100 @@
Function Get-enDomainControllerInfo
{
<#
.SYNOPSIS
Get a list of domain controllers
.DESCRIPTION
Will provide a list of domain controllers in your current domain.
Optionally you can also request a discovery of the "closest" one.
.PARAMETER ComputerName
Retrieve information about the specified domain controller.
This is a RegEx match so you can match multiple domain controllers with your pattern.
.PARAMETER Discover
Use Discover to return the information of the closest domain controller.
.EXAMPLE
Get-enDomainControllerInfo
Retrieve a list of all domain controllers in your domain.
.EXAMPLE
Get-enDomainControllerInfo -Computer 01
Retrieve a list of all domain controllers with "01" in their name.
.EXAMPLE
Get-enDomainControllerInfo -Discover
Retrieve the name of the closest domain controller.
.NOTES
#>
[CmdletBinding(DefaultParameterSetName = 'all')]
Param (
[Parameter(Position = 0, ParameterSetName = 'dc')]
[string]$ComputerName,
[Parameter(ParameterSetName = 'all')]
[switch]$Discover
)
begin
{
$DirectoryContext = [DirectoryServices.ActiveDirectory.DirectoryContext]::New('Domain')
$SelectProperties = 'Name', 'Forest', 'Domain', 'SiteName', 'Roles', 'CurrentTime', 'HighestCommittedUsn', 'OSVersion'
}
process
{
If ($Discover)
{
$LocatorFlag = [DirectoryServices.ActiveDirectory.LocatorOptions]::ForceRediscovery
$Info = ([DirectoryServices.ActiveDirectory.DomainController]::FindOne($DirectoryContext, $LocatorFlag) | Select-Object -Property $SelectProperties)
}
elseif ($ComputerName)
{
$Info = ([DirectoryServices.ActiveDirectory.DomainController]::FindAll($DirectoryContext) | Where-Object -Property Name -Match -Value $ComputerName | Select-Object -Property $SelectProperties)
}
else
{
$Info = ([DirectoryServices.ActiveDirectory.DomainController]::FindAll($DirectoryContext) | Select-Object -Property $SelectProperties)
}
}
end
{
$Info
}
}
#region LICENSE
<#
BSD 3-Clause License
Copyright (c) 2021, enabling Technology
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#>
#endregion LICENSE
#region DISCLAIMER
<#
DISCLAIMER:
- Use at your own risk, etc.
- This is open-source software, if you find an issue try to fix it yourself. There is no support and/or warranty in any kind
- This is a third-party Software
- The developer of this Software is NOT sponsored by or affiliated with Microsoft Corp (MSFT) or any of its subsidiaries in any way
- The Software is not supported by Microsoft Corp (MSFT)
- By using the Software, you agree to the License, Terms, and any Conditions declared and described above
- If you disagree with any of the terms, and any conditions declared: Just delete it and build your own solution
#>
#endregion DISCLAIMER

View File

@@ -0,0 +1,148 @@
#requires -Version 3.0 -Modules ActiveDirectory
function Get-enADFSMORole
{
<#
.SYNOPSIS
Retrieve the FSMO Role in the Forest/Domain
.DESCRIPTION
Retrieve the FSMO Role in the Forest/Domain of Active Directory
.PARAMETER Credential
Specify the alternative credential to use
.EXAMPLE
Get-enADFSMORole
Retrieve the FSMO Role in the Forest/Domain of Active Directory
.EXAMPLE
Get-enADFSMORole -Credential (Get-Credential)
Retrieve the FSMO Role in the Forest/Domain of Active Directory
.NOTES
Releasenotes:
1.0.1 2019-07-26 Refactored, License change to BSD 3-Clause
1.0.0 2019-01-01 Initial Version
THIS CODE IS MADE AVAILABLE AS IS, WITHOUT WARRANTY OF ANY KIND. THE ENTIRE RISK OF THE USE OR THE RESULTS FROM THE USE OF THIS CODE REMAINS WITH THE USER.
Dependencies:
Active Directory PowerShell Module
.LINK
https://www.enatec.io
.LINK
Get-ADForest
.LINK
Get-ADDomain
#>
[CmdletBinding(ConfirmImpact = 'None')]
[OutputType([psobject])]
param
(
[Parameter(ValueFromPipeline,
ValueFromPipelineByPropertyName)]
[System.Management.Automation.Credential()]
[Alias('RunAs')]
[pscredential]
$Credential = [pscredential]::Empty
)
begin
{
$Properties = $null
}
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
}
}
catch
{
#region ErrorHandler
# get error record
[Management.Automation.ErrorRecord]$e = $_
# retrieve information about runtime error
$info = [PSCustomObject]@{
Exception = $e.Exception.Message
Reason = $e.CategoryInfo.Reason
Target = $e.CategoryInfo.TargetName
Script = $e.InvocationInfo.ScriptName
Line = $e.InvocationInfo.ScriptLineNumber
Column = $e.InvocationInfo.OffsetInLine
}
$info | Out-String | Write-Verbose
Write-Error -Message ($info.Exception) -ErrorAction Stop
# Only here to catch a global ErrorAction overwrite
break
#endregion ErrorHandler
}
}
end
{
$Properties
}
}
#region LICENSE
<#
BSD 3-Clause License
Copyright (c) 2021, enabling Technology
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#>
#endregion LICENSE
#region DISCLAIMER
<#
DISCLAIMER:
- Use at your own risk, etc.
- This is open-source software, if you find an issue try to fix it yourself. There is no support and/or warranty in any kind
- This is a third-party Software
- The developer of this Software is NOT sponsored by or affiliated with Microsoft Corp (MSFT) or any of its subsidiaries in any way
- The Software is not supported by Microsoft Corp (MSFT)
- By using the Software, you agree to the License, Terms, and any Conditions declared and described above
- If you disagree with any of the terms, and any conditions declared: Just delete it and build your own solution
#>
#endregion DISCLAIMER

View File

@@ -0,0 +1,189 @@
function Get-enADForestInformation
{
<#
.SYNOPSIS
Retrieve information about an Active Directory Forest
.DESCRIPTION
Retrieve information about an Active Directory Forest
.PARAMETER ForestName
Forest name to retrieve information about
.PARAMETER Credential
Credential to use for retrieval
.EXAMPLE
PS ~> Get-enADForestInformation
Retrieve information about the current Active Directory Forest
.EXAMPLE
PS ~> Get-enADForestInformation | Select-Object ApplicationPartitions
Retrieve information about Application Partitions from the current Active Directory Forest
.EXAMPLE
PS ~> Get-enADForestInformation | Select-Object GlobalCatalogs
Retrieve als Global Catalog Servers from the current Active Directory Forest
.EXAMPLE
PS ~> (Get-enADForestInformation) | Select-Object -ExpandProperty GlobalCatalogs
Retrieve als Global Catalog Servers from the current Active Directory Forest. More details then the above example, cause it will show all the details for each Global Catalog Servers.
.EXAMPLE
PS ~> Get-enADForestInformation | Select-Object NamingRoleOwner
Retrieve information about the Naming master Roles holder from the current Active Directory Forest
.EXAMPLE
PS ~> (Get-enADForestInformation).Sites
Retrieve information about Active Directory Sites from the current Active Directory Forest
.EXAMPLE
PS ~> Get-enADForestInformation -Credential (Get-Credential)
Retrieve information about the current Active Directory Forest, with special credentials (e.g. RunAs)
.EXAMPLE
PS ~> Get-enADForestInformation -ForestName Value
Retrieve information about Active Directory Forest specified in Value
.EXAMPLE
PS ~> Get-enADForestInformation -ForestName Value -Credential Value
Retrieve information about Active Directory Forest specified in Value, with special credentials (e.g. RunAs)
.OUTPUTS
psobject
.INPUTS
String
pscredential
.NOTES
Releasenotes:
1.0.1 2019-07-26 Refactored, License change to BSD 3-Clause
1.0.0 2019-01-01 Initial Version
THIS CODE IS MADE AVAILABLE AS IS, WITHOUT WARRANTY OF ANY KIND. THE ENTIRE RISK OF THE USE OR THE RESULTS FROM THE USE OF THIS CODE REMAINS WITH THE USER.
.LINK
https://www.enatec.io
.LINK
Get-ADForest
#>
[CmdletBinding(ConfirmImpact = 'None')]
[OutputType([psobject])]
param
(
[Parameter(ValueFromPipeline,
ValueFromPipelineByPropertyName)]
[Alias('Forest')]
[string]
$ForestName = ([DirectoryServices.ActiveDirectory.Forest]::GetCurrentForest().Name.ToString()),
[Parameter(ValueFromPipeline,
ValueFromPipelineByPropertyName)]
[System.Management.Automation.Credential()]
[pscredential]
$Credential
)
begin
{
# Cleanup
$output = $null
$ActiveDirectoryContext = $null
}
process
{
try
{
if ($Credential)
{
$credentialUser = ($Credential.UserName.ToString())
$credentialPassword = ($Credential.GetNetworkCredential().Password.ToString())
$ActiveDirectoryContext = (New-Object -TypeName System.DirectoryServices.ActiveDirectory.DirectoryContext -ArgumentList ('forest', $ForestName, $credentialUser, $credentialPassword))
# Cleanup
$credentialUser = $null
$credentialPassword = $null
}
else
{
$ActiveDirectoryContext = (New-Object -TypeName System.DirectoryServices.ActiveDirectory.DirectoryContext -ArgumentList ('forest', $ForestName))
}
$output = ([DirectoryServices.ActiveDirectory.Forest]::GetForest($ActiveDirectoryContext))
}
catch
{
#region ErrorHandler
# get error record
[Management.Automation.ErrorRecord]$e = $_
# retrieve information about runtime error
$info = [PSCustomObject]@{
Exception = $e.Exception.Message
Reason = $e.CategoryInfo.Reason
Target = $e.CategoryInfo.TargetName
Script = $e.InvocationInfo.ScriptName
Line = $e.InvocationInfo.ScriptLineNumber
Column = $e.InvocationInfo.OffsetInLine
}
$info | Out-String | Write-Verbose
Write-Error -Message ($info.Exception) -ErrorAction Stop
# Only here to catch a global ErrorAction overwrite
break
#endregion ErrorHandler
}
}
end
{
$output
# Cleanup
$output = $null
$ActiveDirectoryContext = $null
}
}
#region LICENSE
<#
BSD 3-Clause License
Copyright (c) 2021, enabling Technology
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#>
#endregion LICENSE
#region DISCLAIMER
<#
DISCLAIMER:
- Use at your own risk, etc.
- This is open-source software, if you find an issue try to fix it yourself. There is no support and/or warranty in any kind
- This is a third-party Software
- The developer of this Software is NOT sponsored by or affiliated with Microsoft Corp (MSFT) or any of its subsidiaries in any way
- The Software is not supported by Microsoft Corp (MSFT)
- By using the Software, you agree to the License, Terms, and any Conditions declared and described above
- If you disagree with any of the terms, and any conditions declared: Just delete it and build your own solution
#>
#endregion DISCLAIMER

View File

@@ -0,0 +1,158 @@
function Get-enADGPOReplication
{
<#
.SYNOPSIS
Retrieve one or all the GPO and report their DSVersions and SysVolVersions
.DESCRIPTION
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-enADGPOReplication -GPOName "Default Domain Policy"
Retrieve one GPO and report their DSVersions and SysVolVersions (Users and Computers)
.EXAMPLE
Get-enADGPOReplication -All
Retrieve all the GPO and report their DSVersions and SysVolVersions (Users and Computers)
.NOTES
Releasenotes:
1.0.1 2019-07-26 Refactored, License change to BSD 3-Clause
1.0.0 2019-01-01 Initial Version
THIS CODE IS MADE AVAILABLE AS IS, WITHOUT WARRANTY OF ANY KIND. THE ENTIRE RISK OF THE USE OR THE RESULTS FROM THE USE OF THIS CODE REMAINS WITH THE USER.
Dependencies:
Active Directory PowerShell Module
.LINK
https://www.enatec.io
.LINK
Get-ADDomainController
.LINK
Get-GPO
#>
[CmdletBinding(DefaultParameterSetName = 'All',
ConfirmImpact = 'None')]
param
(
[Parameter(ParameterSetName = 'One', HelpMessage = 'Specify the name of the GPO',
Mandatory,
ValueFromPipeline,
ValueFromPipelineByPropertyName)]
[ValidateNotNullOrEmpty()]
[Alias('GPO')]
[String[]]
$GPOName,
[Parameter(ParameterSetName = 'All',
ValueFromPipeline,
ValueFromPipelineByPropertyName)]
[Switch]
$All
)
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
}
}
}
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
}
}
}
}
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)"
}
}
}
}
#region LICENSE
<#
BSD 3-Clause License
Copyright (c) 2021, enabling Technology
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#>
#endregion LICENSE
#region DISCLAIMER
<#
DISCLAIMER:
- Use at your own risk, etc.
- This is open-source software, if you find an issue try to fix it yourself. There is no support and/or warranty in any kind
- This is a third-party Software
- The developer of this Software is NOT sponsored by or affiliated with Microsoft Corp (MSFT) or any of its subsidiaries in any way
- The Software is not supported by Microsoft Corp (MSFT)
- By using the Software, you agree to the License, Terms, and any Conditions declared and described above
- If you disagree with any of the terms, and any conditions declared: Just delete it and build your own solution
#>
#endregion DISCLAIMER

View File

@@ -0,0 +1,210 @@
function Get-enADGroupChange
{
<#
.SYNOPSIS
Retrieve information about changed Active Directory groups
.DESCRIPTION
Retrieve information about one, or more, changed Active Directory groups
.PARAMETER Server
Active DirectoryDomain Controller to querry.
Default is the logon server
.PARAMETER MonitorGroup
Group to monitor, multi value is supported.
Defaults to all Groups with admins.
Specifies an Active Directory object by providing one of the following property values. The identifier in
parentheses is the LDAP display name for the attribute.
Distinguished Name
Example: CN=DOM-ADM,OU=groups,OU=asia,DC=corp,DC=contoso,DC=com
GUID (objectGUID)
Example: 599c3d2e-f72d-4d20-8a88-030d99495f20
The cmdlet searches the default naming context or partition to find the object. If two or more objects are
found, the cmdlet returns a non-terminating error.
.PARAMETER Hour
Period to query, value in hours
.EXAMPLE
Get-enADGroupChange
Retrieve information about changed Active Directory groups
.EXAMPLE
Get-enADGroupChange -MonitorGroup 'DOM-ADM'
Retrieve information about changes to theActive Directory group DOM-ADM
.EXAMPLE
Get-enADGroupChange -Server DC03
Retrieve information about changed Active Directory groups on DC03
.EXAMPLE
Get-enADGroupChange -Hour 72
Retrieve information about Active Directory groups that have been changed within the last 72 hours
.EXAMPLE
Get-enADGroupChange -Server DC02 -Hour 96
Retrieve information about Active Directory groups that have been changed within the last 96 hours on DC02
.OUTPUTS
psobject
.NOTES
Releasenotes:
1.0.1 2019-07-26 Refactored, License change to BSD 3-Clause
1.0.0 2019-01-01 Initial Version
THIS CODE IS MADE AVAILABLE AS IS, WITHOUT WARRANTY OF ANY KIND. THE ENTIRE RISK OF THE USE OR THE RESULTS FROM THE USE OF THIS CODE REMAINS WITH THE USER.
.INPUTS
String
Int
.LINK
https://www.enatec.io
.LINK
Get-ADDomainController
.LINK
Get-ADGroup
.LINK
Get-ADReplicationAttributeMetadata
.LINK
Get-Date
#>
[CmdletBinding(ConfirmImpact = 'None')]
[OutputType([psobject])]
param
(
[Parameter(ValueFromPipeline,
ValueFromPipelineByPropertyName)]
[ValidateNotNullOrEmpty()]
[Alias('DomainController')]
[string]
$Server = (Get-ADDomainController -Discover | Select-Object -ExpandProperty HostName),
[Parameter(ValueFromPipeline,
ValueFromPipelineByPropertyName)]
[ValidateNotNullOrEmpty()]
[Alias('Group')]
[string[]]
$MonitorGroup = (Get-ADGroup -Filter ' AdminCount -eq 1 ' -Server $Server | Select-Object -ExpandProperty ObjectGUID),
[Parameter(ValueFromPipeline,
ValueFromPipelineByPropertyName)]
[Alias('Period')]
[int]
$Hour = 24
)
begin
{
# Create a new object
$Members = @()
Write-Verbose -Message ('Processing group {0} via Server {1}' -f $MonitorGroup, $Server)
}
process
{
try
{
foreach ($SingleGroup in $MonitorGroup)
{
Write-Verbose -Message ('Processing group {0}' -f $SingleGroup)
# Querry the info and add to the Object
$Members += (Get-ADReplicationAttributeMetadata -Server $Server -Object $SingleGroup -ShowAllLinkedValues | Where-Object -FilterScript {
$_.IsLinkValue
} | Select-Object -Property @{
name = 'GroupDN'
expression = {
$SingleGroup.DistinguishedName
}
}, @{
name = 'GroupName'
expression = {
$SingleGroup.Name
}
}, *)
}
# Filter
$Members | Where-Object -FilterScript {
$_.LastOriginatingChangeTime -gt (Get-Date).AddHours(-1 * $Hour)
}
}
catch
{
#region ErrorHandler
# get error record
[Management.Automation.ErrorRecord]$e = $_
# retrieve information about runtime error
$info = [PSCustomObject]@{
Exception = $e.Exception.Message
Reason = $e.CategoryInfo.Reason
Target = $e.CategoryInfo.TargetName
Script = $e.InvocationInfo.ScriptName
Line = $e.InvocationInfo.ScriptLineNumber
Column = $e.InvocationInfo.OffsetInLine
}
$info | Out-String | Write-Verbose
Write-Error -Message ($info.Exception) -ErrorAction Stop
# Only here to catch a global ErrorAction overwrite
break
#endregion ErrorHandler
}
}
end
{
$Members = $null
}
}
#region LICENSE
<#
BSD 3-Clause License
Copyright (c) 2021, enabling Technology
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#>
#endregion LICENSE
#region DISCLAIMER
<#
DISCLAIMER:
- Use at your own risk, etc.
- This is open-source software, if you find an issue try to fix it yourself. There is no support and/or warranty in any kind
- This is a third-party Software
- The developer of this Software is NOT sponsored by or affiliated with Microsoft Corp (MSFT) or any of its subsidiaries in any way
- The Software is not supported by Microsoft Corp (MSFT)
- By using the Software, you agree to the License, Terms, and any Conditions declared and described above
- If you disagree with any of the terms, and any conditions declared: Just delete it and build your own solution
#>
#endregion DISCLAIMER

View File

@@ -0,0 +1,378 @@
function Get-enADObject
{
<#
.SYNOPSIS
Export Active Directory Objects
.DESCRIPTION
Export Active Directory Objects
.PARAMETER ADObjectFilter
Provide specific AD Objects to report on. Otherwise, all AD Objects will be reported. Please review the examples provided.
.PARAMETER DetailedReport
Provides a full report of all attributes. Otherwise, only a refined report will be given.
.EXAMPLE
PS ~> Get-enADObject | Export-Csv C:\scripts\PowerShell\Reports\ADObjects.csv -notypeinformation -encoding UTF8
Export Active Directory Objects
.EXAMPLE
PS ~> {objectclass -eq "publicFolder"} | Get-enADObject -DetailedReport | Export-Csv C:\scripts\PowerShell\Reports\PFs.csv -NoTypeInformation -Encoding UTF8
Export Active Directory Objects
.EXAMPLE
PS ~> '{proxyaddresses -like "*contoso.com"}' | Get-enADObject | Export-Csv C:\scripts\PowerShell\Reports\ADObjects.csv -notypeinformation -encoding UTF8
Export Active Directory Objects
.EXAMPLE
PS ~> '{proxyaddresses -like "*contoso.com"}' | Get-enADObject -DetailedReport | Export-Csv C:\scripts\PowerShell\Reports\ADObjects_Detailed.csv -notypeinformation -encoding UTF8
Export Active Directory Objects
.OUTPUTS
PSObject
.NOTES
Releasenotes:
1.0.1 2019-07-26 Refactored, License change to BSD 3-Clause
1.0.0 2019-01-01 Initial Version
THIS CODE IS MADE AVAILABLE AS IS, WITHOUT WARRANTY OF ANY KIND. THE ENTIRE RISK OF THE USE OR THE RESULTS FROM THE USE OF THIS CODE REMAINS WITH THE USER.
Dependencies:
Active Directory PowerShell Module
.LINK
https://www.enatec.io
.LINK
Get-ADObject
#>
[CmdletBinding(ConfirmImpact = 'None')]
[OutputType([psobject])]
param (
[switch]
$DetailedReport,
[Parameter(ValueFromPipeline)]
[string[]]
$ADObjectFilter
)
begin
{
if ($DetailedReport)
{
$Selectproperties = @(
'DisplayName', 'UserPrincipalName', 'mail', 'CN', 'mailNickname', 'Name', 'GivenName', 'Surname', 'StreetAddress'
'City', 'State', 'Country', 'PostalCode', 'Company', 'Title', 'Department', 'Description', 'OfficePhone'
'MobilePhone', 'HomePhone', 'Fax', 'SamAccountName', 'DistinguishedName', 'Office', 'Enabled'
'whenChanged', 'whenCreated', 'adminCount', 'AccountNotDelegated', 'AllowReversiblePasswordEncryption'
'CannotChangePassword', 'Deleted', 'DoesNotRequirePreAuth', 'HomedirRequired', 'isDeleted', 'LockedOut'
'mAPIRecipient', 'mDBUseDefaults', 'MNSLogonAccount', 'msExchHideFromAddressLists'
'msNPAllowDialin', 'PasswordExpired', 'PasswordNeverExpires', 'PasswordNotRequired', 'ProtectedFromAccidentalDeletion'
'SmartcardLogonRequired', 'TrustedForDelegation', 'TrustedToAuthForDelegation', 'UseDESKeyOnly', 'logonHours'
'msExchMailboxGuid', 'replicationSignature', 'AccountExpirationDate', 'AccountLockoutTime', 'Created', 'createTimeStamp'
'LastBadPasswordAttempt', 'LastLogonDate', 'Modified', 'modifyTimeStamp', 'msTSExpireDate', 'PasswordLastSet'
'msExchMailboxSecurityDescriptor', 'nTSecurityDescriptor', 'BadLogonCount', 'codePage', 'countryCode'
'deletedItemFlags', 'dLMemDefault', 'garbageCollPeriod', 'instanceType', 'msDS-SupportedEncryptionTypes'
'msDS-User-Account-Control-Computed', 'msExchALObjectVersion', 'msExchMobileMailboxFlags', 'msExchRecipientDisplayType'
'msExchUserAccountControl', 'primaryGroupID', 'replicatedObjectVersion', 'sAMAccountType', 'sDRightsEffective'
'userAccountControl', 'accountExpires', 'lastLogonTimestamp', 'lockoutTime', 'msExchRecipientTypeDetails', 'msExchVersion'
'pwdLastSet', 'uSNChanged', 'uSNCreated', 'ObjectGUID', 'objectSid', 'SID', 'autoReplyMessage', 'CanonicalName'
'displayNamePrintable', 'Division', 'EmployeeID', 'EmployeeNumber', 'HomeDirectory', 'HomeDrive', 'homeMDB', 'homeMTA'
'HomePage', 'Initials', 'LastKnownParent', 'legacyExchangeDN', 'LogonWorkstations'
'Manager', 'msExchHomeServerName', 'msExchUserCulture', 'msTSLicenseVersion', 'msTSManagingLS'
'ObjectCategory', 'ObjectClass', 'Organization', 'OtherName', 'POBox', 'PrimaryGroup'
'ProfilePath', 'ScriptPath', 'sn', 'textEncodedORAddress', 'userParameters'
)
$CalculatedProps = @(
@{
n = 'OU'
e = {
$_.DistinguishedName -replace '^.+?,(?=(OU|CN)=)'
}
},
@{
n = 'proxyAddresses'
e = {
($_.proxyAddresses | Where-Object -FilterScript {
$_ -ne $null
}) -join '|'
}
},
@{
n = 'altRecipientBL'
e = {
($_.altRecipientBL | Where-Object -FilterScript {
$_ -ne $null
}) -join ';'
}
},
@{
n = 'AuthenticationPolicy'
e = {
($_.AuthenticationPolicy | Where-Object -FilterScript {
$_ -ne $null
}) -join ';'
}
},
@{
n = 'AuthenticationPolicySilo'
e = {
($_.AuthenticationPolicySilo | Where-Object -FilterScript {
$_ -ne $null
}) -join ';'
}
},
@{
n = 'Certificates'
e = {
($_.Certificates | Where-Object -FilterScript {
$_ -ne $null
}) -join ';'
}
},
@{
n = 'CompoundIdentitySupported'
e = {
($_.CompoundIdentitySupported | Where-Object -FilterScript {
$_ -ne $null
}) -join ';'
}
},
@{
n = 'dSCorePropagationData'
e = {
($_.dSCorePropagationData | Where-Object -FilterScript {
$_ -ne $null
}) -join ';'
}
},
@{
n = 'KerberosEncryptionType'
e = {
($_.KerberosEncryptionType | Where-Object -FilterScript {
$_ -ne $null
}) -join ';'
}
},
@{
n = 'managedObjects'
e = {
($_.managedObjects | Where-Object -FilterScript {
$_ -ne $null
}) -join ';'
}
},
@{
n = 'MemberOf'
e = {
($_.MemberOf | Where-Object -FilterScript {
$_ -ne $null
}) -join ';'
}
},
@{
n = 'msExchADCGlobalNames'
e = {
($_.msExchADCGlobalNames | Where-Object -FilterScript {
$_ -ne $null
}) -join ';'
}
},
@{
n = 'msExchPoliciesExcluded'
e = {
($_.msExchPoliciesExcluded | Where-Object -FilterScript {
$_ -ne $null
}) -join ';'
}
},
@{
n = 'PrincipalsAllowedToDelegateToAccount'
e = {
($_.PrincipalsAllowedToDelegateToAccount | Where-Object -FilterScript {
$_ -ne $null
}) -join ';'
}
},
@{
n = 'protocolSettings'
e = {
($_.protocolSettings | Where-Object -FilterScript {
$_ -ne $null
}) -join ';'
}
},
@{
n = 'publicDelegatesBL'
e = {
($_.publicDelegatesBL | Where-Object -FilterScript {
$_ -ne $null
}) -join ';'
}
},
@{
n = 'securityProtocol'
e = {
($_.securityProtocol | Where-Object -FilterScript {
$_ -ne $null
}) -join ';'
}
},
@{
n = 'ServicePrincipalNames'
e = {
($_.ServicePrincipalNames | Where-Object -FilterScript {
$_ -ne $null
}) -join ';'
}
},
@{
n = 'showInAddressBook'
e = {
($_.showInAddressBook | Where-Object -FilterScript {
$_ -ne $null
}) -join ';'
}
},
@{
n = 'SIDHistory'
e = {
($_.SIDHistory | Where-Object -FilterScript {
$_ -ne $null
}) -join ';'
}
},
@{
n = 'userCertificate'
e = {
($_.userCertificate | Where-Object -FilterScript {
$_ -ne $null
}) -join ';'
}
}
)
$ExtensionAttribute = @(
'extensionAttribute1', 'extensionAttribute2', 'extensionAttribute3', 'extensionAttribute4', 'extensionAttribute5'
'extensionAttribute6', 'extensionAttribute7', 'extensionAttribute8', 'extensionAttribute9', 'extensionAttribute10'
'extensionAttribute11', 'extensionAttribute12', 'extensionAttribute13', 'extensionAttribute14', 'extensionAttribute15'
)
}
else
{
$Props = @(
'DisplayName', 'UserPrincipalName', 'mail', 'CN', 'mailNickname', 'Name', 'GivenName', 'Surname', 'StreetAddress',
'City', 'State', 'Country', 'PostalCode', 'Company', 'Title', 'Department', 'Description', 'OfficePhone'
'MobilePhone', 'HomePhone', 'Fax', 'SamAccountName', 'DistinguishedName', 'Office', 'Enabled'
'whenChanged', 'whenCreated', 'adminCount', 'Memberof', 'msExchPoliciesExcluded', 'proxyAddresses'
)
$Selectproperties = @(
'DisplayName', 'UserPrincipalName', 'mail', 'CN', 'mailNickname', 'Name', 'GivenName', 'Surname', 'StreetAddress',
'City', 'State', 'Country', 'PostalCode', 'Company', 'Title', 'Department', 'Description', 'OfficePhone'
'MobilePhone', 'HomePhone', 'Fax', 'SamAccountName', 'DistinguishedName', 'Office', 'Enabled'
'whenChanged', 'whenCreated', 'adminCount'
)
$CalculatedProps = @(
@{
n = 'proxyAddresses'
e = {
($_.proxyAddresses | Where-Object -FilterScript {
$_ -ne $null
}) -join '|'
}
},
@{
n = 'OU'
e = {
$_.DistinguishedName -replace '^.+?,(?=(OU|CN)=)'
}
},
@{
n = 'MemberOf'
e = {
($_.MemberOf | Where-Object -FilterScript {
$_ -ne $null
}) -join ';'
}
},
@{
n = 'msExchPoliciesExcluded'
e = {
($_.msExchPoliciesExcluded | Where-Object -FilterScript {
$_ -ne $null
}) -join ';'
}
}
)
}
}
process
{
if ($ADObjectFilter)
{
foreach ($CurADObjectFilter in $ADObjectFilter)
{
if (! $DetailedReport)
{
Get-ADObject -Filter $CurADObjectFilter -Properties $Props -ResultSetSize $null | Select-Object -Property ($Selectproperties + $CalculatedProps)
}
else
{
Get-ADObject -Filter $CurADObjectFilter -Properties * -ResultSetSize $null | Select-Object -Property ($Selectproperties + $CalculatedProps + $ExtensionAttribute)
}
}
}
else
{
if (! $DetailedReport)
{
Get-ADObject -Filter * -Properties $Props -ResultSetSize $null | Select-Object -Property ($Selectproperties + $CalculatedProps)
}
else
{
Get-ADObject -Filter * -Properties * -ResultSetSize $null | Select-Object -Property ($Selectproperties + $CalculatedProps + $ExtensionAttribute)
}
}
}
}
#region LICENSE
<#
BSD 3-Clause License
Copyright (c) 2021, enabling Technology
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#>
#endregion LICENSE
#region DISCLAIMER
<#
DISCLAIMER:
- Use at your own risk, etc.
- This is open-source software, if you find an issue try to fix it yourself. There is no support and/or warranty in any kind
- This is a third-party Software
- The developer of this Software is NOT sponsored by or affiliated with Microsoft Corp (MSFT) or any of its subsidiaries in any way
- The Software is not supported by Microsoft Corp (MSFT)
- By using the Software, you agree to the License, Terms, and any Conditions declared and described above
- If you disagree with any of the terms, and any conditions declared: Just delete it and build your own solution
#>
#endregion DISCLAIMER

View File

@@ -0,0 +1,178 @@
function Get-enADServicePrincipalName
{
<#
.SYNOPSIS
Retrieves all Service Principal Names (SPNs)
.DESCRIPTION
Retrieves all Service Principal Names (SPNs) from Active Directory
.INPUTS
NONE
.OUTPUTS
PSObject
.EXAMPLE
PS /> Get-enADServicePrincipalName
Retrieves all Service Principal Names (SPNs) from Active Directory
.EXAMPLE
PS ~> Get-enADServicePrincipalName | Where-Object -FilterScript { $_.ObjectClass -eq 'user' }
Retrieves all user class Service Principal Names (SPNs) from Active Directory
.EXAMPLE
PS ~> Get-enADServicePrincipalName | Where-Object -FilterScript { $_.DNSHostName -like 'server01.contoso.com' }
Retrieves all Service Principal Names (SPNs) for the Server 'server01.contoso.com' from Active Directory
.EXAMPLE
PS ~> Get-enADServicePrincipalName | Where-Object -FilterScript { $_.Name -like '*Krb*' }
Retrieves all Kerberos related Service Principal Names (SPNs) from Active Directory
.EXAMPLE
PS ~> Get-enADServicePrincipalName | Where-Object -FilterScript { $_.SPN -like '*Krb*' }
Retrieves all Kerberos related Service Principal Names (SPNs) from Active Directory
.EXAMPLE
PS ~> Get-enADServicePrincipalName | Export-Csv -Path C:\scripts\PowerShell\Reports\ADServicePrincipalNames.csv
Retrieves all Service Principal Names (SPNs) from Active Directory and export them to a CSV
.NOTES
Releasenotes:
1.0.1 2019-07-26 Refactored, License change to BSD 3-Clause
1.0.0 2019-01-01 Initial Version
THIS CODE IS MADE AVAILABLE AS IS, WITHOUT WARRANTY OF ANY KIND. THE ENTIRE RISK OF THE USE OR THE RESULTS FROM THE USE OF THIS CODE REMAINS WITH THE USER.
Dependencies:
Active Directory PowerShell Module
.LINK
https://www.enatec.io
.LINK
Get-ADObject
#>
[CmdletBinding(ConfirmImpact = 'None')]
[OutputType([psobject])]
param ()
begin
{
# Create a new Object
$AllObject = @()
}
process
{
try
{
# We use Get-ADObject because this seems to be fast enough
$paramGetADObject = @{
Filter = "(objectClass -eq 'user') -or (objectClass -eq 'computer') -and (servicePrincipalName -like '*')"
Properties = 'Name', 'servicePrincipalName', 'DistinguishedName', 'ObjectClass', 'DNSHostName', 'whenCreated'
}
$AllServicePrincipalNames = (Get-ADObject @paramGetADObject)
# Loop over the List we got from Get-ADObject
foreach ($SingleServicePrincipalName in $AllServicePrincipalNames)
{
# Get the values for the Service Principal Name
$ObjectClass = $SingleServicePrincipalName.ObjectClass
$DistinguishedName = $SingleServicePrincipalName.DistinguishedName
$Name = $SingleServicePrincipalName.Name
$whenCreated = $SingleServicePrincipalName.whenCreated
$DNSHostName = $SingleServicePrincipalName.DNSHostName
# Loop over all Service Principal Names - Remeber, there could be more then one Service Principal Names value per record
foreach ($ServicePrincipalName in $SingleServicePrincipalName.servicePrincipalName)
{
# Create a new Object
$SingleObject = (New-Object -TypeName PSObject -Property @{
Name = $Name
SPN = $ServicePrincipalName
ObjectClass = $ObjectClass
DistinguishedName = $DistinguishedName
WhenCreated = $whenCreated
DNSHostName = $DNSHostName
})
# Add the Values to the List
$AllObject += $SingleObject
# Cleanup
$SingleObject = $null
}
}
}
catch
{
#region ErrorHandler
# get error record
[Management.Automation.ErrorRecord]$e = $_
# retrieve information about runtime error
$info = [PSCustomObject]@{
Exception = $e.Exception.Message
Reason = $e.CategoryInfo.Reason
Target = $e.CategoryInfo.TargetName
Script = $e.InvocationInfo.ScriptName
Line = $e.InvocationInfo.ScriptLineNumber
Column = $e.InvocationInfo.OffsetInLine
}
$info | Out-String | Write-Verbose
Write-Error -Message ($info.Exception) -ErrorAction Stop
# Only here to catch a global ErrorAction overwrite
break
#endregion ErrorHandler
}
}
end
{
# Dump
$AllObject
# Cleanup
$AllObject = $null
}
}
#region LICENSE
<#
BSD 3-Clause License
Copyright (c) 2021, enabling Technology
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#>
#endregion LICENSE
#region DISCLAIMER
<#
DISCLAIMER:
- Use at your own risk, etc.
- This is open-source software, if you find an issue try to fix it yourself. There is no support and/or warranty in any kind
- This is a third-party Software
- The developer of this Software is NOT sponsored by or affiliated with Microsoft Corp (MSFT) or any of its subsidiaries in any way
- The Software is not supported by Microsoft Corp (MSFT)
- By using the Software, you agree to the License, Terms, and any Conditions declared and described above
- If you disagree with any of the terms, and any conditions declared: Just delete it and build your own solution
#>
#endregion DISCLAIMER

View File

@@ -0,0 +1,132 @@
function Get-enADSiteAndSubnetInfo
{
<#
.SYNOPSIS
Retrieve Site names, subnets names and descriptions.
.DESCRIPTION
Retrieve Site names, subnets names and descriptions from the Active Directory
.EXAMPLE
PS ~> Get-enADSiteAndSubnetInfo
Retrieve Site names, subnets names and descriptions from the Active Directory
.EXAMPLE
PS ~> Get-enADSiteAndSubnetInfo | Export-Csv -Path C:\scripts\PowerShell\Reports\ADSiteInventory.csv
Retrieve Site names, subnets names and descriptions from the Active Directory
.OUTPUTS
PSObject
.NOTES
Releasenotes:
1.0.1 2019-07-26 Refactored, License change to BSD 3-Clause
1.0.0 2019-01-01 Initial Version
THIS CODE IS MADE AVAILABLE AS IS, WITHOUT WARRANTY OF ANY KIND. THE ENTIRE RISK OF THE USE OR THE RESULTS FROM THE USE OF THIS CODE REMAINS WITH THE USER.
Dependencies:
Active Directory PowerShell Module
.LINK
https://www.enatec.io
#>
[CmdletBinding(ConfirmImpact = 'None')]
[OutputType([psobject])]
param ()
begin
{
Write-Verbose -Message '[BEGIN] Starting Script...'
}
process
{
try
{
# Domain and Sites Information
$Forest = ([DirectoryServices.ActiveDirectory.Forest]::GetCurrentForest())
$SiteInfo = ([DirectoryServices.ActiveDirectory.Forest]::GetCurrentForest().Sites)
# Forest Context
$ForestType = ([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,{0}' -f $Configuration))
$SubnetsContainerchildren = ($SubnetsContainer.Children)
foreach ($item in $SiteInfo)
{
Write-Verbose -Message ('[PROCESS] SITE: {0}' -f $item.name)
$output = @{
Name = $item.name
}
foreach ($i in $item.Subnets.name)
{
Write-Verbose -Message ('[PROCESS] SUBNET: {0}' -f $i)
$output.Subnet = $i
$SubnetAdditionalInfo = $SubnetsContainerchildren.Where( {
$_.name -match $i
})
Write-Verbose -Message ('[PROCESS] SUBNET: {0} - DESCRIPTION: {1}' -f $i, $SubnetAdditionalInfo.Description)
$output.Description = $($SubnetAdditionalInfo.Description)
Write-Verbose -Message '[PROCESS] OUTPUT INFO'
New-Object -TypeName PSObject -Property $output
}
}
}
catch
{
Write-Warning -Message '[PROCESS] Something Wrong Happened'
Write-Warning -Message $Error[0]
}
}
end
{
Write-Verbose -Message '[END] Script Completed!'
}
}
#region LICENSE
<#
BSD 3-Clause License
Copyright (c) 2021, enabling Technology
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#>
#endregion LICENSE
#region DISCLAIMER
<#
DISCLAIMER:
- Use at your own risk, etc.
- This is open-source software, if you find an issue try to fix it yourself. There is no support and/or warranty in any kind
- This is a third-party Software
- The developer of this Software is NOT sponsored by or affiliated with Microsoft Corp (MSFT) or any of its subsidiaries in any way
- The Software is not supported by Microsoft Corp (MSFT)
- By using the Software, you agree to the License, Terms, and any Conditions declared and described above
- If you disagree with any of the terms, and any conditions declared: Just delete it and build your own solution
#>
#endregion DISCLAIMER

View File

@@ -0,0 +1,76 @@
Function Get-enDomainInfo
{
<#
.SYNOPSIS
Retrieve domain information include site details
.EXAMPLE
Get-enDomainInfo
.NOTES
#>
[CmdletBinding()]
Param ()
begin
{
$SelectProperties = 'Name', 'Forest', 'Parent', 'Children', 'DomainMode', 'DomainModeLevel', 'DomainControllers', 'PdcRoleOwner', 'RidRoleOwner', 'InfrastructureRoleOwner', 'Sites'
}
process
{
$CurrentDomain = [DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain()
$null = ($CurrentDomain | Add-Member -MemberType NoteProperty -Name Sites -Value ([DirectoryServices.ActiveDirectory.Forest]::GetCurrentForest().Sites))
$Domain = ($CurrentDomain | Select-Object -Property $SelectProperties)
<#
switch($domainModeLevel)
{
{$domainModeLevel -like "0"} {"2000 Mixed/Native"}
{$domainModeLevel -like "1"} {"2003 Interim"}
{$domainModeLevel -like "2"} {"2003"}
{$domainModeLevel -like "3"} {"2008"}
{$domainModeLevel -like "4"} {"2008 R2"}
{$domainModeLevel -like "5"} {"2012"}
{$domainModeLevel -like "6"} {"2012 R2"}
{$domainModeLevel -like "7"} {"2016"}
default {"Unknown"}
}
#>
}
end
{
$Domain
}
}
#region LICENSE
<#
BSD 3-Clause License
Copyright (c) 2021, enabling Technology
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#>
#endregion LICENSE
#region DISCLAIMER
<#
DISCLAIMER:
- Use at your own risk, etc.
- This is open-source software, if you find an issue try to fix it yourself. There is no support and/or warranty in any kind
- This is a third-party Software
- The developer of this Software is NOT sponsored by or affiliated with Microsoft Corp (MSFT) or any of its subsidiaries in any way
- The Software is not supported by Microsoft Corp (MSFT)
- By using the Software, you agree to the License, Terms, and any Conditions declared and described above
- If you disagree with any of the terms, and any conditions declared: Just delete it and build your own solution
#>
#endregion DISCLAIMER

View File

@@ -0,0 +1,29 @@
BSD 3-Clause License
Copyright (c) 2021, enabling Technology <http://enatec.io>
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@@ -0,0 +1,7 @@
# Legacy Notice
I no longer run Exchange, Skype for Business, or any other Office Server on Premises.
This is my personal [reaction](https://hochwald.net/microsoft-rolls-back-decision-to-take-away-internal-usage-rights-from-partners/) to the changes that Microsoft [announced](https://hochwald.net/microsoft-is-going-to-kill-internal-use-rights-benefit-for-partners/) for the Internal Use Rights (IUR) program. I know that they decided to reverse that changes and in theory, I could still legally use the software. However, I decided to decommission everything licensed under the terms of the Internal Use Rights (IUR) program.
In my opinion, the community always should have some benefits from the Internal Use Rights (IUR) program and/or Action Pack. Now that I decided to drop out, there will be no more such benefits.
I will _no longer maintain_ the scripts related to the Microsoft Office (on Premises) servers. They will remain here, but unmaintained. Fork the repository and maintain or extend them if you like to. The [License](https://github.com/jhochwald/PowerShell-collection/blob/master/LICENSE) allows that easily.

View File

@@ -0,0 +1,413 @@
#requires -Version 3.0 -Modules ActiveDirectory
<#
.SYNOPSIS
Tool that bulk imports or removes User pictures, based on AD Group Membership
.DESCRIPTION
Tool that bulk imports or removes User pictures, based on AD Group Membership
If a user is in both groups, the picture will be removed!
Idea based on my old tool to import Active Directory pictures.
They are a bit to tiny, so I use Exchange now to make them look better in Exchange and Skype.
.PARAMETER AddGroup
Active Directory Group with users that would like to have a picture.
For all Members of this group, the Tool will try to set an image.
.PARAMETER RemGroup
Active Directory Group with users that would like have have the picture removed.
For all Members of this group, the Tool will try to remove the existing image (If set).
.PARAMETER PictureDir
Directory that contains the pictures
.PARAMETER Extension
Extension of the pictures
.PARAMETER workaround
Workaround for Exchange 2016 on Windows Server 2016
.PARAMETER UPNDomain
The default Domain, to add to the UPN
.EXAMPLE
# Use the Groups 'ADDPIXX' and 'NOPIXX' to Set/Remove the User Pictures
# There was an Issue with the User joerg.hochwald (Possible Picture Problem!
PS C:\> .\Set-ADAllUserPicture.ps1 -AddGroup 'ADDPIXX' -RemGroup 'NOPIXX' -PictureDir 'c:\upixx\' -workaround -UPNDomain 'jhochwald.com'
WARNING: Unable to set Image c:\upixx\joerg.hochwald.jpg for User joerg.hochwald
.EXAMPLE
# Use the Groups 'ADDPIXX' and 'NOPIXX' to Set/Remove the User Pictures
# There was an Issue with the User jane.doe - Check that this user has a provissioned Mailbox (on Prem or Cloud)
PS C:\> .\Set-ADAllUserPicture.ps1 -AddGroup 'ADDPIXX' -RemGroup 'NOPIXX' -PictureDir 'c:\upixx\' -workaround -UPNDomain 'jhochwald.com'
WARNING: Unable to handle jane.doe - Check that this user has a valid Mailbox!
.EXAMPLE
# Use the Groups 'ADDPIXX' and 'NOPIXX' to Set/Remove the User Pictures - Everything went well
PS C:\> .\Set-ADAllUserPicture.ps1 -AddGroup 'ADDPIXX' -RemGroup 'NOPIXX' -PictureDir 'c:\upixx\' -workaround -UPNDomain 'jhochwald.com'
WARNING: Unable to handle jane.doe - Check that this user has a valid Mailbox!
.NOTES
TODO: There is no logging! Only the Exchange RBAC logging is in use
TODO: A few error handlers are still missing
If a user is in both groups, the picture will be removed!
Verbose could be very verbose. This is due to the fact, that the complete Exchange logging will be shown!
There are a few possibilities for Warnings and Errors. (Mostly for missing things)
Disclaimer: The code is provided 'as is,' with all possible faults, defects or errors, and without warranty of any kind.
#>
param
(
[Parameter(Mandatory,
ValueFromPipeline,
ValueFromPipelineByPropertyName,
Position = 1,
HelpMessage = 'Active Directory Group with users that would like to have a picture')]
[ValidateNotNullOrEmpty()]
[Alias('positive')]
[string]
$AddGroup,
[Parameter(Mandatory,
ValueFromPipeline,
ValueFromPipelineByPropertyName,
Position = 2,
HelpMessage = 'Active Directory Group with users that would like have have the picture removed.')]
[ValidateNotNullOrEmpty()]
[string]
$RemGroup,
[Parameter(Mandatory,
ValueFromPipeline,
ValueFromPipelineByPropertyName,
Position = 3,
HelpMessage = 'Directory that contains the picures')]
[ValidateNotNullOrEmpty()]
[Alias('PixxDir')]
[string]
$PictureDir,
[Parameter(ValueFromPipeline,
ValueFromPipelineByPropertyName,
Position = 5)]
[Alias('defaultDomain')]
[string]
$UPNDomain,
[Parameter(ValueFromPipeline,
ValueFromPipelineByPropertyName,
Position = 4)]
[ValidateSet('png', 'jpg', 'gif', 'bmp')]
[ValidateNotNullOrEmpty()]
[string]
$Extension = 'jpg',
[switch]
$workaround = $false
)
begin
{
if ($workaround)
{
# Unsupported Workaround according to https://hochwald.net/workaround-for-get-help-issue-with-exchange-2016-on-windows-server-2016/
$null = (Add-PSSnapin -Name Microsoft.Exchange.Management.PowerShell.SnapIn)
}
# Cleanup
$AddUserPixx = $null
$NoUserPixx = $null
# Check the source directory string and fix it if needed
if (-not ($PictureDir).EndsWith('\'))
{
# Fix it
$PictureDir = $PictureDir + '\'
$paramWriteVerbose = @{
Message = 'Fixed the Source Directory String!'
}
Write-Verbose @paramWriteVerbose
}
try
{
$paramGetADGroupMember = @{
Identity = $AddGroup
ErrorAction = 'Stop'
WarningAction = 'SilentlyContinue'
}
$AddUserPixx = (Get-ADGroupMember @paramGetADGroupMember | Select-Object -ExpandProperty samaccountname)
}
catch
{
$paramWriteError = @{
Message = ('Unable to find {0}' -f $AddGroup)
ErrorAction = 'Stop'
}
Write-Error @paramWriteError
return
}
try
{
$paramGetADGroupMember = @{
Identity = $RemGroup
ErrorAction = 'Stop'
WarningAction = 'SilentlyContinue'
}
$NoUserPixx = (Get-ADGroupMember @paramGetADGroupMember | Select-Object -ExpandProperty samaccountname)
}
catch
{
$paramWriteError = @{
Message = ('Unable to find {0}' -f $AddGroup)
ErrorAction = 'Stop'
}
Write-Error @paramWriteError
return
}
function Test-ValidEmail
{
<#
.SYNOPSIS
Simple Function to check if a String is a valid Mail
.DESCRIPTION
Simple Function to check if a String is a valid Mail and return a Bool
.PARAMETER address
Address String to Check
.EXAMPLE
# Not a valid String
PS C:\> Test-ValidEmail -address 'Joerg.Hochwald'
False
.EXAMPLE
# Valid String
PS C:\> Test-ValidEmail -address 'Joerg.Hochwald@outlook.com'
True
.NOTES
Disclaimer: The code is provided 'as is,' with all possible faults, defects or errors, and without warranty of any kind.
Author: Joerg Hochwald
#>
[OutputType([bool])]
param
(
[Parameter(Mandatory,
HelpMessage = 'Address String to Check')]
[ValidateNotNullOrEmpty()]
[string]
$address
)
process
{
($address -as [mailaddress]).Address -eq $address -and $address -ne $null
}
}
}
process
{
if (-not ($AddUserPixx.samaccountname))
{
$paramWriteVerbose = @{
Message = ('The AD Group {0} has no members.' -f $AddGroup)
}
Write-Verbose @paramWriteVerbose
}
else
{
# Add a counter
$AddUserPixxCount = (($AddUserPixx.samaccountname).count)
$paramWriteVerbose = @{
Message = ('The AD Group {0} has {1} members.' -f $AddGroup, $AddUserPixxCount)
}
Write-Verbose @paramWriteVerbose
foreach ($AddUser in $AddUserPixx.samaccountname)
{
if (($NoUserPixx.samaccountname) -notcontains $AddUser)
{
# Check the UPN and Fix it, if possible
if (-not (Test-ValidEmail -address ($AddUser)))
{
if (-not ($UPNDomain))
{
# Whoopsie
$paramWriteError = @{
Message = 'UPN Default Domain not set but needed!'
ErrorAction = 'Stop'
}
Write-Error @paramWriteError
}
else
{
# Let us fix this
$AddUserUPN = ($AddUser + '@' + $UPNDomain)
}
}
# Build the Full Image Path
$SingleUserPicture = ($PictureDir + $AddUser + '.' + $Extension)
# Check if Picture exists
$paramTestPath = @{
Path = $SingleUserPicture
ErrorAction = 'Stop'
WarningAction = 'SilentlyContinue'
}
if (Test-Path @paramTestPath)
{
try
{
$paramSetUserPhoto = @{
Identity = $AddUserUPN
PictureData = ([IO.File]::ReadAllBytes($SingleUserPicture))
Confirm = $false
ErrorAction = 'Stop'
WarningAction = 'SilentlyContinue'
}
$null = (Set-UserPhoto @paramSetUserPhoto)
}
catch
{
$paramWriteWarning = @{
Message = ('Unable to set Image {0} for User {1}' -f $SingleUserPicture, $AddUser)
ErrorAction = 'SilentlyContinue'
}
Write-Warning @paramWriteWarning
}
}
else
{
$paramWriteWarning = @{
Message = ('The Image {0} for User {1} was not found' -f $SingleUserPicture, $AddUser)
ErrorAction = 'SilentlyContinue'
}
Write-Warning @paramWriteWarning
}
}
else
{
$paramWriteVerbose = @{
Message = ('Sorry, User {0} is member of {1} and {2}' -f $AddUser, $AddGroup, $RemGroup)
}
Write-Verbose @paramWriteVerbose
}
}
}
if (-not ($NoUserPixx.samaccountname))
{
$paramWriteVerbose = @{
Message = ('The AD Group {0} has no members.' -f $RemGroup)
}
Write-Verbose @paramWriteVerbose
}
else
{
# Add a counter
$NoUserPixxCount = (($NoUserPixx.samaccountname).count)
$paramWriteVerbose = @{
Message = ('The AD Group {0} has {1} members.' -f $RemGroup, $NoUserPixxCount)
}
Write-Verbose @paramWriteVerbose
foreach ($NoUser in $NoUserPixx.samaccountname)
{
# Check the UPN and Fix it, if possible
if (-not (Test-ValidEmail -address ($NoUser)))
{
if (-not ($UPNDomain))
{
# Whoopsie
$paramWriteError = @{
Message = 'UPN Default Domain not set but needed!'
ErrorAction = 'Stop'
}
Write-Error @paramWriteError
}
else
{
# Let us fix this
$NoUserUPN = ($NoUser + '@' + $UPNDomain)
}
}
$paramSetUserPhoto = @{
Identity = $NoUserUPN
Confirm = $false
ErrorAction = 'Stop'
WarningAction = 'SilentlyContinue'
}
try
{
$null = (Remove-UserPhoto @paramSetUserPhoto)
}
catch
{
$paramWriteWarning = @{
Message = ('Unable to handle {0} - Check that this user has a valid Mailbox!' -f $NoUser)
ErrorAction = 'SilentlyContinue'
}
Write-Warning @paramWriteWarning
}
}
}
}
end
{
# Cleaniup
$AddUserPixx = $null
$NoUserPixx = $null
$AddUserPixxCount = $null
$NoUserPixxCount = $null
# Do a garbage collection: Call the .NET function to cleanup some stuff
$null = ([GC]::Collect())
}
#region LICENSE
<#
BSD 3-Clause License
Copyright (c) 2021, enabling Technology
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#>
#endregion LICENSE
#region DISCLAIMER
<#
DISCLAIMER:
- Use at your own risk, etc.
- This is open-source software, if you find an issue try to fix it yourself. There is no support and/or warranty in any kind
- This is a third-party Software
- The developer of this Software is NOT sponsored by or affiliated with Microsoft Corp (MSFT) or any of its subsidiaries in any way
- The Software is not supported by Microsoft Corp (MSFT)
- By using the Software, you agree to the License, Terms, and any Conditions declared and described above
- If you disagree with any of the terms, and any conditions declared: Just delete it and build your own solution
#>
#endregion DISCLAIMER

View File

@@ -0,0 +1,118 @@
#requires -Version 3.0 -Modules ActiveDirectory
function Set-ADServerUsage
{
<#
.SYNOPSIS
Set all Active Directory related commands to use a special kind of server
.DESCRIPTION
By default the Active Directory related commands search for a DC. By default I want to make
use of the closest one. When I make BULK operations, I would like to use the Server with
the PDC role. This becomes handy often!
.PARAMETER pdc
Use the Active Directory Server who holds the PDC role.
.EXAMPLE
# Use the closest Server
PS> Set-ADServerUsage
.EXAMPLE
# Use the Server with the PDC role
PS> Set-ADServerUsage -pdc
.EXAMPLE
# When it comes to scripts that do bulk operations, especially bulk loads and manipulation,
# I use the following within the Script:
if (Get-Command Set-ADServerUsage -ErrorAction SilentlyContinue)
{
Set-ADServerUsage -pdc
}
.NOTES
I use this helper function in my PROFILE. Therefore, some things a bit special.
Who want's an error message every time a window opens under normal circumstances?
#>
param
(
[Parameter(ValueFromPipeline,
ValueFromPipelineByPropertyName,
Position = 1)]
[switch]
$pdc
)
begin
{
# Defaults
$SC = 'SilentlyContinue'
# Cleanup
$dc = $null
}
process
{
<#
The following would do the trick:
#requires -Modules ActiveDirectory
But I don't want any error messages, so I decided to use this old-school way to figure
out if we are capable do what I want.
#>
if ((Get-Command -Name Get-ADDomain -ErrorAction $SC) -and (Get-Command -Name Get-ADDomainController -ErrorAction $SC) )
{
if ($pdc)
{
# Use the PDC instead
$dc = ((Get-ADDomain -ErrorAction $SC -WarningAction $SC).PDCEmulator)
}
else
{
# Use the closest DC
$dc = (Get-ADDomainController -Discover -NextClosestSite -ErrorAction $SC -WarningAction $SC)
}
# Skip everything if we do NOT have the proper information.
<#
Under normal circumstances this is pretty useless, but I use some virtual machines that have the RSAT tools installed, but they are not domain joined.
The fore I make this check. If all the systems that have the RSAT installed are domain joined, this test is obsolete.
#>
if ($dc)
{
# Make use of the Server from above
$PSDefaultParameterValues.add('*-AD*:Server', "$dc")
}
}
}
}
#region LICENSE
<#
BSD 3-Clause License
Copyright (c) 2021, enabling Technology
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#>
#endregion LICENSE
#region DISCLAIMER
<#
DISCLAIMER:
- Use at your own risk, etc.
- This is open-source software, if you find an issue try to fix it yourself. There is no support and/or warranty in any kind
- This is a third-party Software
- The developer of this Software is NOT sponsored by or affiliated with Microsoft Corp (MSFT) or any of its subsidiaries in any way
- The Software is not supported by Microsoft Corp (MSFT)
- By using the Software, you agree to the License, Terms, and any Conditions declared and described above
- If you disagree with any of the terms, and any conditions declared: Just delete it and build your own solution
#>
#endregion DISCLAIMER

View File

@@ -0,0 +1,255 @@
function Invoke-AdvancedInstallerUpdate
{
<#
.SYNOPSIS
Sample function to rebuild a given Advanced Installer Project
.DESCRIPTION
Rebuild a given Advanced Installer Project.
Sample script to update the build Number from our build server and create a new MSI installer.
.PARAMETER Project
Advanced installer project name (the name of the Project file, without the AIP extension).
Example: DummyProduct for DummyProduct.aip
.PARAMETER Path
Specifies the path to Advanced Installer Project File.
.PARAMETER Version
Version of the new build.
.EXAMPLE
PS C:\> Invoke-AdvancedInstallerUpdate -Project 'DummyProduct' -Path 'x:\dev\projects\DummyProduct\' -Version '1.0.3'
.NOTES
Sample Project
.LINK
https://www.advancedinstaller.com/user-guide/powershell-automation.html
#>
[CmdletBinding(ConfirmImpact = 'None')]
param
(
[Parameter(Mandatory,
ValueFromPipeline,
ValueFromPipelineByPropertyName,
Position = 0,
HelpMessage = 'Advanced installer project name (the name of the Project file, without the AIP extension).')]
[ValidateNotNullOrEmpty()]
[Alias('ProjectName', 'aipName')]
[string]
$Project,
[Parameter(Mandatory,
ValueFromPipeline,
ValueFromPipelineByPropertyName,
Position = 1,
HelpMessage = 'Specifies the path to Advanced Installer Project File.')]
[ValidateNotNullOrEmpty()]
[Alias('aipPath')]
[string]
$Path,
[Parameter(Mandatory,
ValueFromPipeline,
ValueFromPipelineByPropertyName,
Position = 2,
HelpMessage = 'Version of the new build.')]
[ValidateNotNullOrEmpty()]
[Alias('aipVersion')]
[string]
$Version
)
begin
{
# Create the full path of the Avanced Installer Project file
$AdvancedInstallerProjectName = $Path + $Project + '.aip'
# Check if the File exists
if (-not (Test-Path -Path $AdvancedInstallerProjectName -ErrorAction SilentlyContinue))
{
#region ErrorHandler
$paramWriteError = @{
Message = ('The given File {0} was not found' -f $AdvancedInstallerProjectName)
TargetObject = $AdvancedInstallerProjectName
Category = 'ObjectNotFound'
RecommendedAction = 'Check filename'
ErrorAction = 'Stop'
}
Write-Error @paramWriteError
# Only here to catch a global ErrorAction overwrite
break
#endregion ErrorHandler
}
# New Version number
$AdvancedInstallerProjectVersion = $Version
}
process
{
# Cleanup
$AdvancedInstallerProject = $null
# Creates a new PS object for Advanced Installer interaction
$AdvancedInstaller = (New-Object -ComObject AdvancedInstaller)
# Load the Advanced Installer object
try
{
$AdvancedInstallerProject = $AdvancedInstaller.LoadProject($AdvancedInstallerProjectName)
}
catch
{
#region ErrorHandler
# get error record
[Management.Automation.ErrorRecord]$e = $_
# retrieve information about runtime error
$info = [PSCustomObject]@{
Exception = $e.Exception.Message
Reason = $e.CategoryInfo.Reason
Target = $e.CategoryInfo.TargetName
Script = $e.InvocationInfo.ScriptName
Line = $e.InvocationInfo.ScriptLineNumber
Column = $e.InvocationInfo.OffsetInLine
}
$info | Out-String | Write-Verbose
Write-Error -Message ($info.Exception) -ErrorAction Stop
# Only here to catch a global ErrorAction overwrite
break
#endregion ErrorHandler
}
if ($AdvancedInstallerProject)
{
# Modidy the version number
$AdvancedInstallerProject.ProductDetails.Version = $AdvancedInstallerProjectVersion
try
{
# Build the project
$AdvancedInstallerProjectBuild = ($AdvancedInstallerProject.Build())
Write-Verbose -Message $AdvancedInstallerProjectBuild
# Save the modified file
try
{
# Note: Remove the $null if you would like to see the output
$null = ($AdvancedInstallerProject.SaveAs($AdvancedInstallerProjectName))
}
catch
{
#region ErrorHandler
# get error record
[Management.Automation.ErrorRecord]$e = $_
# retrieve information about runtime error
$info = [PSCustomObject]@{
Exception = $e.Exception.Message
Reason = $e.CategoryInfo.Reason
Target = $e.CategoryInfo.TargetName
Script = $e.InvocationInfo.ScriptName
Line = $e.InvocationInfo.ScriptLineNumber
Column = $e.InvocationInfo.OffsetInLine
}
$info | Out-String | Write-Verbose
Write-Error -Message ($info.Exception) -ErrorAction Stop
# Only here to catch a global ErrorAction overwrite
break
#endregion ErrorHandler
}
}
catch
{
Write-Error -Message 'Build failed'
}
finally
{
# Cleanup
$AdvancedInstallerProject = $null
$AdvancedInstaller = $null
}
}
else
{
#region ErrorHandler
$paramWriteError = @{
Message = ('Unable to load {0}' -f $AdvancedInstallerProjectName)
TargetObject = $AdvancedInstallerProjectName
Category = 'InvalidData'
RecommendedAction = 'Check file'
ErrorAction = 'Stop'
}
Write-Error @paramWriteError
# Only here to catch a global ErrorAction overwrite
break
#endregion ErrorHandler
}
}
end
{
# Create a filter for the MSI
$AdvancedInstallerProjectMSI = $Project + '.msi'
# Cleanup
$AdvancedInstallerProjectMSIPath = $null
# Loop over the returned object and try to find the MSI
$AdvancedInstallerProjectMSIPath = ($AdvancedInstallerProjectBuild.Split("`n") | ForEach-Object {
if ($_ -match $AdvancedInstallerProjectMSI)
{
$_
}
})
# TODO: The method is a bit crappy
if ($AdvancedInstallerProjectMSIPath)
{
Write-Host -Object $AdvancedInstallerProjectMSIPath
}
else
{
Write-Warning -Message 'New MSI was not found' -WarningAction Continue
}
}
}
#region LICENSE
<#
BSD 3-Clause License
Copyright (c) 2021, enabling Technology
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#>
#endregion LICENSE
#region DISCLAIMER
<#
DISCLAIMER:
- Use at your own risk, etc.
- This is open-source software, if you find an issue try to fix it yourself. There is no support and/or warranty in any kind
- This is a third-party Software
- The developer of this Software is NOT sponsored by or affiliated with Microsoft Corp (MSFT) or any of its subsidiaries in any way
- The Software is not supported by Microsoft Corp (MSFT)
- By using the Software, you agree to the License, Terms, and any Conditions declared and described above
- If you disagree with any of the terms, and any conditions declared: Just delete it and build your own solution
#>
#endregion DISCLAIMER

View File

@@ -0,0 +1,29 @@
BSD 3-Clause License
Copyright (c) 2021, enabling Technology <http://enatec.io>
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@@ -0,0 +1,199 @@
#requires -Version 3.0 -Modules AzureAD
<#
.SYNOPSIS
Script to monitor and return large number of user devices in Azure Active Directory.
.DESCRIPTION
Script to monitor and return large number of user devices in Active Directory.
The default limit in Azure is 20 devices
.PARAMETER All
If true, return all users.
.PARAMETER HighDeviceCount
Enter the threshold for devices that you want to return
.EXAMPLE
Get-AzureADUserDevices.ps1 -HighDeviceCount 15 -All $true
.EXAMPLE
Get-AzureADUserDevices.ps1 -HighDeviceCount 5 -All $true
.NOTES
Reworked version of Ben Whitmore Get-UserDevices that use the AzureAD module instead of the MsolService module
.LINK
https://github.com/byteben/AzureAD/blob/master/Get-UserDevices.ps1
#>
[CmdletBinding(ConfirmImpact = 'None')]
param
(
[Parameter(ValueFromPipeline,
ValueFromPipelineByPropertyName,
Position = 0)]
[ValidateNotNullOrEmpty()]
[int]
$HighDeviceCount = 15,
[Parameter(ValueFromPipeline,
ValueFromPipelineByPropertyName,
Position = 1)]
[bool]
$All
)
begin
{
# Defaults
$STP = 'Stop'
# Set some default
if (-not ($HighDeviceCount))
{
$HighDeviceCount = 15
}
# Connect to Azure Active Directory, if needed
if ($AzureActiveDirectoryConnection.Account -eq $null)
{
try
{
$Global:AzureActiveDirectoryConnection = (Connect-AzureAD -ErrorAction $STP)
}
catch
{
#region ErrorHandler
# get error record
[Management.Automation.ErrorRecord]$e = $_
# retrieve information about runtime error
$info = [PSCustomObject]@{
Exception = $e.Exception.Message
Reason = $e.CategoryInfo.Reason
Target = $e.CategoryInfo.TargetName
Script = $e.InvocationInfo.ScriptName
Line = $e.InvocationInfo.ScriptLineNumber
Column = $e.InvocationInfo.OffsetInLine
}
$info | Out-String | Write-Verbose
Write-Error -Message ($info.Exception) -ErrorAction $STP
# Only here to catch a global ErrorAction overwrite
break
#endregion ErrorHandler
}
}
# Initialize Array to hold users and number of devices
$DeviceCountHigh = @()
try
{
# Splatting
$paramGetAzureADUser = @{
filter = "userType eq 'Member'"
All = $All
ErrorAction = $STP
}
# Get list of users from Azure Active Directory
$Users = (Get-AzureADUser @paramGetAzureADUser | Select-Object -Property UserPrincipalName, ObjectId)
# Splatting
$paramGetAzureADDevice = @{
All = $true
ErrorAction = $STP
}
# Get a list of Devices and the ownership information from the Azure Active Directory
$Devices = (Get-AzureADDevice @paramGetAzureADDevice | Get-AzureADDeviceRegisteredOwner -ErrorAction $STP)
}
catch
{
#region ErrorHandler
# get error record
[Management.Automation.ErrorRecord]$e = $_
# retrieve information about runtime error
$info = @{
Exception = $e.Exception.Message
Reason = $e.CategoryInfo.Reason
Target = $e.CategoryInfo.TargetName
Script = $e.InvocationInfo.ScriptName
Line = $e.InvocationInfo.ScriptLineNumber
Column = $e.InvocationInfo.OffsetInLine
}
$info | Out-String | Write-Verbose
Write-Error -Message ($info.Exception) -ErrorAction $STP
# Only here to catch a global ErrorAction overwrite
break
#endregion ErrorHandler
}
}
process
{
foreach ($User in $Users)
{
# For each user returned, count their Registered Devices
$Device = ($Devices | Where-Object {
$_.UserPrincipalName -eq $User.UserPrincipalName
} | Measure-Object)
# If the number of registered devices measured is high, create a new PSObject
if ($Device.Count -ge $HighDeviceCount)
{
# Create a new PSObject
$DeviceCountMember = @()
# Fill the values
$DeviceCountMember = (New-Object -TypeName PSObject)
$DeviceCountMember | Add-Member -MemberType NoteProperty -Name 'UserPrincipalName' -Value $User.UserPrincipalName
$DeviceCountMember | Add-Member -MemberType NoteProperty -Name 'DeviceCount' -Value $Device.Count
# Add to the PSObject
$DeviceCountHigh += $DeviceCountMember
}
}
}
end
{
# Display Users with high number of devices
$DeviceCountHigh | Sort-Object -Property DeviceCount -Descending
}
#region LICENSE
<#
BSD 3-Clause License
Copyright (c) 2021, enabling Technology
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#>
#endregion LICENSE
#region DISCLAIMER
<#
DISCLAIMER:
- Use at your own risk, etc.
- This is open-source software, if you find an issue try to fix it yourself. There is no support and/or warranty in any kind
- This is a third-party Software
- The developer of this Software is NOT sponsored by or affiliated with Microsoft Corp (MSFT) or any of its subsidiaries in any way
- The Software is not supported by Microsoft Corp (MSFT)
- By using the Software, you agree to the License, Terms, and any Conditions declared and described above
- If you disagree with any of the terms, and any conditions declared: Just delete it and build your own solution
#>
#endregion DISCLAIMER

View File

@@ -0,0 +1,29 @@
BSD 3-Clause License
Copyright (c) 2021, enabling Technology <http://enatec.io>
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@@ -0,0 +1,87 @@
#requires -Version 1.0
function Get-DsRegStatusInfo
{
<#
.SYNOPSIS
Wrapper function for the dsregcmd command
.DESCRIPTION
Wrapper function for the dsregcmd command
Nothing fancy, but it should convert the plain text output of dsregcmd to a PSObject
.EXAMPLE
PS C:\> Get-DsRegStatusInfo
Returns a PSObject with the values of dsregcmd
.EXAMPLE
PS C:\> $AADInfo = (Get-DsRegStatusInfo | Select-Object -Property AzureAdJoined,WorkplaceJoined)
PS C:\> if ( ($AADInfo.AzureAdJoined -ne 'YES') -and ($AADInfo.WorkplaceJoined -ne 'YES') ) {throw 'Not AzureAD bound'}
Check if the system is joined to the AzureAD (fully or just WorkplaceJoined)
.EXAMPLE
PS C:\> $AADInfo = (Get-DsRegStatusInfo | Select-Object -Property AzureAdJoined, WorkplaceJoined)
PS C:\> if ($AADInfo.AzureAdJoined -eq 'YES') {'AzureAd Joined'} elseif ($AADInfo.WorkplaceJoined -eq 'YES') {'Workplace Joined'} else {'Unknown'}
Check if the system is joined to the AzureAD (fully or just WorkplaceJoined)
.NOTES
Replaced my old ConvertFrom-String based wrapper implementation, this is more flexible
.LINK
http://hochwald.net
#>
[CmdletBinding(ConfirmImpact = 'None')]
[OutputType([psobject])]
param ()
begin
{
$DsRegCmdPlain = (& "$env:windir\system32\dsregcmd.exe" /status)
$DsRegStatusInfo = (New-Object -TypeName PSObject)
}
process
{
$DsRegCmdPlain | Select-String -Pattern ' *[A-z]+ : [A-z]+ *' | ForEach-Object -Process {
$null = (Add-Member -InputObject $DsRegStatusInfo -MemberType NoteProperty -Name (([String]$_).Trim() -split ' : ')[0] -Value (([String]$_).Trim() -split ' : ')[1] -ErrorAction SilentlyContinue)
}
}
end
{
$DsRegStatusInfo
}
}
#region LICENSE
<#
BSD 3-Clause License
Copyright (c) 2021, enabling Technology
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#>
#endregion LICENSE
#region DISCLAIMER
<#
DISCLAIMER:
- Use at your own risk, etc.
- This is open-source software, if you find an issue try to fix it yourself. There is no support and/or warranty in any kind
- This is a third-party Software
- The developer of this Software is NOT sponsored by or affiliated with Microsoft Corp (MSFT) or any of its subsidiaries in any way
- The Software is not supported by Microsoft Corp (MSFT)
- By using the Software, you agree to the License, Terms, and any Conditions declared and described above
- If you disagree with any of the terms, and any conditions declared: Just delete it and build your own solution
#>
#endregion DISCLAIMER

View File

@@ -0,0 +1,152 @@
#requires -Version 2.0 -Modules BitLocker
#requires -RunAsAdministrator
<#
.SYNOPSIS
Backup the BitLocker Recovery Information to the Azure Active Directory
.DESCRIPTION
Backup the BitLocker Recovery Information to the Azure Active Directory
If the Boot Drive is not encrypted, the Script will try to enable the quick protection
.EXAMPLE
PS C:\> .\Invoke-BackupBitlockerRecoveryKey.ps1
.EXAMPLE
PS C:\> $AADInfo = (Get-DsRegStatusInfo | Select-Object -Property AzureAdJoined,WorkplaceJoined)
PS C:\> if ( ($AADInfo.AzureAdJoined -ne 'YES') -and ($AADInfo.WorkplaceJoined -ne 'YES') ) {throw 'Not AzureAD bound'} else {.\Invoke-BackupBitlockerRecoveryKey.ps1}
You may want to check if the device is AzureAD joined with Get-DsRegStatusInfo first
.NOTES
Quick and relative dirty solution for a challenge I had in the last couple of days
.LINK
Get-DsRegStatusInfo
.LINK
http://hochwald.net
#>
[CmdletBinding(ConfirmImpact = 'Low')]
param ()
begin
{
# Defaults
$LogName = 'Application'
$STP = 'Stop'
$SCT = 'SilentlyContinue'
$LogSource = 'enAutomate'
# Register the event log source
$null = (New-EventLog -LogName $LogName -Source $LogSource -ErrorAction $SCT)
}
process
{
try
{
# Get BitLocker Volume info
$BitLockerVolumeInfo = (Get-BitLockerVolume -ErrorAction $STP | Where-Object -FilterScript {
$_.VolumeType -eq 'OperatingSystem'
})
# Get the Mount Point
$BootDrive = $BitLockerVolumeInfo.MountPoint
# Check if the drive is encrypted
if ($BitLockerVolumeInfo.ProtectionStatus -ne 'On')
{
$InfoMessage = ('Enable BitLocker for ' + $BootDrive)
Write-Verbose -Message $InfoMessage
$null = (Write-EventLog -LogName $LogName -Source $LogSource -EntryType Information -EventId 1000 -Message $InfoMessage -ErrorAction $SCT)
# Now we try to activate BitLocker (-UsedSpaceOnly is not perfect, but much faster in this case
$null = (Enable-BitLocker -MountPoint $BootDrive -EncryptionMethod XtsAes128 -UsedSpaceOnly -SkipHardwareTest -RecoveryPasswordProtector -Confirm:$false -ErrorAction $STP)
}
# Get the correct ID (The one from the RecoveryPassword)
$BitLockerKeyProtectorId = ($BitLockerVolumeInfo.KeyProtector | Where-Object -FilterScript {
$_.KeyProtectorType -eq 'RecoveryPassword'
} | Select-Object -ExpandProperty KeyProtectorId)
# Check if we have a recovery password/id
if ($BitLockerKeyProtectorId)
{
# Do the backup towards AzureAD
$null = (BackupToAAD-BitLockerKeyProtector -MountPoint $BootDrive -KeyProtectorId $BitLockerKeyProtectorId -Confirm:$false -ErrorAction $STP)
$InfoMessage = ('The Recovery Infor for ' + $BootDrive + ' was saved to the Azure Active Directory')
Write-Verbose -Message $InfoMessage
$null = (Write-EventLog -LogName $LogName -Source $LogSource -EntryType Information -EventId 1000 -Message $InfoMessage -ErrorAction $SCT)
}
else
{
$WarningMessage = ('No Recorvery Information for ' + $BootDrive + ' found...')
Write-Warning -Message $WarningMessage
$null = (Write-EventLog -LogName $LogName -Source $LogSource -EntryType Warning -EventId 1001 -Message $WarningMessage -ErrorAction $SCT)
}
}
catch
{
#region ErrorHandler
# Get error record
[Management.Automation.ErrorRecord]$e = $_
# retrieve information about runtime error
$info = @{
Exception = $e.Exception.Message
Reason = $e.CategoryInfo.Reason
Target = $e.CategoryInfo.TargetName
Script = $e.InvocationInfo.ScriptName
Line = $e.InvocationInfo.ScriptLineNumber
Column = $e.InvocationInfo.OffsetInLine
}
# Error Stack
$info | Out-String | Write-Verbose
# Save to the Event Log
$null = (Write-EventLog -LogName $LogName -Source $LogSource -EntryType Error -EventId 1001 -Message ($info.Exception) -ErrorAction $SCT)
# Just display the info on continue with the rest of the list
$paramWriteError = @{
Message = ($info.Exception)
Exception = $info.Exception
TargetObject = $info.Target
ErrorAction = 'Stop'
WarningAction = 'Continue'
}
Write-Error @paramWriteError
}
}
#region LICENSE
<#
BSD 3-Clause License
Copyright (c) 2021, enabling Technology
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#>
#endregion LICENSE
#region DISCLAIMER
<#
DISCLAIMER:
- Use at your own risk, etc.
- This is open-source software, if you find an issue try to fix it yourself. There is no support and/or warranty in any kind
- This is a third-party Software
- The developer of this Software is NOT sponsored by or affiliated with Microsoft Corp (MSFT) or any of its subsidiaries in any way
- The Software is not supported by Microsoft Corp (MSFT)
- By using the Software, you agree to the License, Terms, and any Conditions declared and described above
- If you disagree with any of the terms, and any conditions declared: Just delete it and build your own solution
#>
#endregion DISCLAIMER

View File

@@ -0,0 +1,29 @@
BSD 3-Clause License
Copyright (c) 2021, enabling Technology <http://enatec.io>
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@@ -0,0 +1,144 @@
#requires -Version 2.0 -Modules BitLocker
#requires -RunAsAdministrator
<#
.SYNOPSIS
Create a new BitLocker Recovery Key
.DESCRIPTION
Create a new BitLocker Recovery Key
We will just create a new one, but we will not show it.
You should store it into the AzureAD, or a least in the Active Directory
.EXAMPLE
PS C:\> .\New-BitlockerRecoveryKey.ps1
.NOTES
Quick and relative dirty solution for a challange I had in the last couple of days
By the way: Only the Boot Drive is supported by default.
.LINK
Add-BitLockerKeyProtector
.LINK
http://hochwald.net
#>
[CmdletBinding(ConfirmImpact = 'Low')]
param ()
begin
{
# Defaults
$LogName = 'Application'
$STP = 'Stop'
$SCT = 'SilentlyContinue'
$LogSource = 'enAutomate'
# Register the event log source
$null = (New-EventLog -LogName $LogName -Source $LogSource -ErrorAction $SCT)
}
process
{
# Get BitLocker Volume info
$BitLockerVolumeInfo = (Get-BitLockerVolume | Where-Object -FilterScript {
$_.VolumeType -eq 'OperatingSystem'
})
# Get the Mount Point
$BootDrive = $BitLockerVolumeInfo.MountPoint
# Get the Key
$KeyProtectors = $BitLockerVolumeInfo.KeyProtector
# Check if the Boot Drive is encrypted
if (($BitLockerVolumeInfo.VolumeStatus -eq 'FullyDecrypted') -or ($BitLockerVolumeInfo.ProtectionStatus -eq 'Off') -or (-not ($KeyProtectors)))
{
Write-Warning -Message ('Please Exceute: "Enable-BitLocker -MountPoint {0}"' -f $BootDrive)
break
}
else
{
foreach ($KeyProtector in $KeyProtectors)
{
if ($KeyProtector.KeyProtectorType -eq 'RecoveryPassword')
{
try
{
# Remove the existing Recovery Password
$null = (Remove-BitLockerKeyProtector -MountPoint $BootDrive -KeyProtectorId $KeyProtector.KeyProtectorId -ErrorAction $STP)
# Just add a new Recovery Password without showing it here. We store than in the AzureAD anyway!
$null = (Add-BitLockerKeyProtector -MountPoint $BootDrive -RecoveryPasswordProtector -WarningAction SilentlyContinue)
# If we get this far, eveything has worked, write a success to the event log
$InfoText = 'Changed the BitLocker Recovery Password for ' + $BootDrive + ' successfully'
Write-EventLog -LogName $LogName -Source $LogSource -EntryType Information -EventId 1000 -Message $InfoText
Write-Output -InputObject $InfoText
}
catch
{
#region ErrorHandler
# Get error record
[Management.Automation.ErrorRecord]$e = $_
# retrieve information about runtime error
$info = @{
Exception = $e.Exception.Message
Reason = $e.CategoryInfo.Reason
Target = $e.CategoryInfo.TargetName
Script = $e.InvocationInfo.ScriptName
Line = $e.InvocationInfo.ScriptLineNumber
Column = $e.InvocationInfo.OffsetInLine
}
# Error Stack
$info | Out-String | Write-Verbose
# Save to the Event Log
$null = (Write-EventLog -LogName $LogName -Source $LogSource -EntryType Error -EventId 1001 -Message ($info.Exception) -ErrorAction $SCT)
# Just display the info on continue with the rest of the list
$paramWriteError = @{
Message = ($info.Exception)
Exception = $info.Exception
TargetObject = $info.Target
ErrorAction = $STP
WarningAction = 'Continue'
}
Write-Error @paramWriteError
}
}
}
}
}
#region LICENSE
<#
BSD 3-Clause License
Copyright (c) 2021, enabling Technology
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#>
#endregion LICENSE
#region DISCLAIMER
<#
DISCLAIMER:
- Use at your own risk, etc.
- This is open-source software, if you find an issue try to fix it yourself. There is no support and/or warranty in any kind
- This is a third-party Software
- The developer of this Software is NOT sponsored by or affiliated with Microsoft Corp (MSFT) or any of its subsidiaries in any way
- The Software is not supported by Microsoft Corp (MSFT)
- By using the Software, you agree to the License, Terms, and any Conditions declared and described above
- If you disagree with any of the terms, and any conditions declared: Just delete it and build your own solution
#>
#endregion DISCLAIMER

View File

@@ -0,0 +1,420 @@
#requires -Version 2.0
<#
.SYNOPSIS
Exchange Server Logs Cleanup
.DESCRIPTION
Cleanup some Exchange Server logs.
.EXAMPLE
PS C:\> .\CleanupExchangeLogs.ps1
.NOTES
Releasenotes:
1.0.1 2019-07-08: Move the delete process to the dedicated Invoke-CleanupOldFiles function
1.0.0 2019-02-04: Internal Release
THIS CODE IS MADE AVAILABLE AS IS, WITHOUT WARRANTY OF ANY KIND. THE ENTIRE RISK OF THE USE OR THE RESULTS FROM THE USE OF THIS CODE REMAINS WITH THE USER.
.LINK
Invoke-CleanupOldFiles
#>
[CmdletBinding(ConfirmImpact = 'Low')]
param ()
begin
{
# You can change the number of days here
$days = 30
#region PowerShell2WorkArounds
<#
The following stuff is a workaround to make everything compatible to PowerShell 2.0
Old, but some still have the old crap on the Exchange server running, sorry!
#>
#region RequiredModuleWorkAround
if (Get-Module -Name webadministration -ListAvailable -ErrorAction SilentlyContinue)
{
try
{
$null = (Import-Module -Name webadministration -Force -ErrorAction Stop)
}
catch
{
#region ErrorHandler
# get error record
[Management.Automation.ErrorRecord]$e = $_
# retrieve information about runtime error
$info = @{
Exception = $e.Exception.Message
Reason = $e.CategoryInfo.Reason
Target = $e.CategoryInfo.TargetName
Script = $e.InvocationInfo.ScriptName
Line = $e.InvocationInfo.ScriptLineNumber
Column = $e.InvocationInfo.OffsetInLine
}
$info | Out-String | Write-Verbose
Write-Error -Message ($info.Exception) -ErrorAction Stop
# Only here to catch a global ErrorAction overwrite
break
#endregion ErrorHandler
}
}
else
{
#region ErrorHandler
$paramWriteError = @{
Message = 'The required Module (webadministration) is missing!'
Category = 'ObjectNotFound'
ErrorAction = 'Stop'
}
Write-Error @paramWriteError
# Only here to catch a global ErrorAction overwrite
break
#endregion ErrorHandler
}
#endregion RequiredModuleWorkAround
#region RunAsAdministrator
function Test-Administrator
{
<#
.SYNOPSIS
Check if this is an elevated shell
.DESCRIPTION
Check if this is an elevated shell.
In Powershell 4.0 it can be replaced with:
#Requires -RunAsAdministrator
.EXAMPLE
PS C:\> Test-Administrator
.NOTES
In Powershell 4.0 it can be replaced with: Requires -RunAsAdministrator
License: Public Domain
#>
[CmdletBinding(ConfirmImpact = 'None')]
[OutputType([bool])]
param ()
process
{
$user = [Security.Principal.WindowsIdentity]::GetCurrent()
(New-Object -TypeName Security.Principal.WindowsPrincipal -ArgumentList $user).IsInRole([Security.Principal.WindowsBuiltinRole]::Administrator)
}
}
if ((Test-Administrator) -ne $true)
{
#region ErrorHandler
Write-Error -Message 'The current Windows PowerShell session is not running as Administrator. Start Windows PowerShell by using the Run as Administrator option, and then try running the script again.' -Category NotEnabled -ErrorAction Stop
# Only here to catch a global ErrorAction overwrite
break
#endregion ErrorHandler
}
#endregion RunAsAdministrator
#endregion PowerShell2WorkArounds
# Cleanup
$LogDirList = $null
# Create a new List
$LogDirList = (New-Object -TypeName System.Collections.Generic.List[System.Object])
# Add static Directories to the new list
if ($env:ExchangeInstallPath)
{
$LogDirList.Add($env:ExchangeInstallPath + 'Logging\')
# Another possible Directory
#$LogDirList.Add($env:ExchangeInstallPath + 'Bin\Search\Ceres\Diagnostics\Logs\')
}
else
{
Write-Warning -Message 'This is not a Exchange Server!'
}
# Get a list of all IIS Websites and add to the new list
$AllIISSites = (Get-Website)
if ($AllIISSites)
{
# Loop over the IIS Site list
foreach ($SingleWebSite in $AllIISSites)
{
# Cleanup
$IISLogDirectory = $null
# Get the Log-Directory from the IIS Info
$IISLogDirectory = ($SingleWebSite.logfile.directory)
<#
Replace the returned %SystemDrive% with your system drive.
This is your BOOT Drive!!! Usually it is C:
#>
if ($IISLogDirectory -match '%SystemDrive%')
{
Write-Verbose -Message 'Mangle the SystemDrive within the variable...'
$IISLogDirectory = ($IISLogDirectory -replace '%SystemDrive%', 'C:')
}
# Add the log Directory to the List
$LogDirList.Add($IISLogDirectory)
}
}
else
{
Write-Warning -Message 'No IIS Log-Directory found!'
}
# Make all entries in the List unique
$LogDirList = ($LogDirList | Sort-Object | Get-Unique)
#region Invoke-CleanupOldFiles
function Invoke-CleanupOldFiles
{
<#
.SYNOPSIS
Remove files older then a given number of days
.DESCRIPTION
Remove files older then a given number of days.
Mostly used within cleanup Tasks.
.PARAMETER Path
Path to search.
.PARAMETER Age
Age of files to remove, in days.
Defaults to 30
.EXAMPLE
PS C:\> Invoke-CleanupoldFiles -Path 'C:\inetpub\logs\LogFiles'
.EXAMPLE
PS C:\> Invoke-CleanupoldFiles -Path 'C:\inetpub\logs\LogFiles' -Age 14
.NOTES
Releasenotes:
1.0.1 2019-07-08: Rework and splatting
1.0.0 2019-02-04: Internal Release
THIS CODE IS MADE AVAILABLE AS IS, WITHOUT WARRANTY OF ANY KIND. THE ENTIRE RISK OF THE USE OR THE RESULTS FROM THE USE OF THIS CODE REMAINS WITH THE USER.
.LINK
Get-ChildItem
.LINK
Test-Path
.LINK
Get-Date
#>
[CmdletBinding(ConfirmImpact = 'Low')]
param
(
[Parameter(Mandatory = $true,
ValueFromPipeline = $true,
ValueFromPipelineByPropertyName = $true,
Position = 0,
HelpMessage = 'Path to search.')]
[ValidateNotNullOrEmpty()]
[Alias('TargetFolder')]
[string]
$Path,
[Parameter(ValueFromPipeline = $true,
ValueFromPipelineByPropertyName = $true,
Position = 1)]
[ValidateNotNullOrEmpty()]
[Alias('Days')]
[int]
$Age = 30
)
process
{
if (Test-Path -Path $Path)
{
# Save the date to use it for the compare
$Now = (Get-Date)
# Today minus given days
$LastWrite = $Now.AddDays(-$days)
# Splatting the parameters
$paramGetChildItem = @{
Path = $Path
Include = '*.log', '*.blg'
Recurse = $true
}
# Find all Files to Delete (e.g. older then the given value)
$Files = (Get-ChildItem @paramGetChildItem | Where-Object -FilterScript {
(-not ($_.PSIsContainer)) -and ($_.LastWriteTime -le $LastWrite)
} | Select-Object -ExpandProperty fullname)
# Loop over the list of Files
foreach ($File in $Files)
{
# Support for WhatIf and Verbose
if ($pscmdlet.ShouldProcess($File, 'Remove'))
{
# Splatting the parameters
$paramRemoveItem = @{
Path = $File
ErrorAction = 'SilentlyContinue'
Force = $true
WhatIf = $false
}
# Remove the files that we found
$null = (Remove-Item @paramRemoveItem)
}
}
}
else
{
#region ErrorHandler
# get error record
[Management.Automation.ErrorRecord]$e = $_
# retrieve information about runtime error
$info = @{
Exception = $e.Exception.Message
Reason = $e.CategoryInfo.Reason
Target = $e.CategoryInfo.TargetName
Script = $e.InvocationInfo.ScriptName
Line = $e.InvocationInfo.ScriptLineNumber
Column = $e.InvocationInfo.OffsetInLine
}
$info | Out-String | Write-Verbose
Write-Error -Message ($info.Exception) -ErrorAction Stop
# Only here to catch a global ErrorAction overwrite
break
#endregion ErrorHandler
}
}
}
#endregion Invoke-CleanupOldFiles
}
process
{
# Do we have a lif of directories?
if ($LogDirList)
{
# Loop over the List of Directories
foreach ($LogDir in $LogDirList)
{
Write-Verbose -Message "Removing logs from $LogDir older then $days days"
try
{
# Do we have a DAY value
if ($days)
{
# Splatting the parameters
$paramInvokeCleanupoldFiles = @{
Path = $LogDir
Age = $days
ErrorAction = 'Stop'
verbose = $true
}
}
else
{
# Splatting the parameters
$paramInvokeCleanupoldFiles = @{
Path = $LogDir
ErrorAction = 'Stop'
verbose = $true
}
}
# Invoke the internal Fun
Invoke-CleanupOldFiles @paramInvokeCleanupoldFiles
}
catch
{
#region ErrorHandler
# get error record
[Management.Automation.ErrorRecord]$e = $_
# retrieve information about runtime error
$info = @{
Exception = $e.Exception.Message
Reason = $e.CategoryInfo.Reason
Target = $e.CategoryInfo.TargetName
Script = $e.InvocationInfo.ScriptName
Line = $e.InvocationInfo.ScriptLineNumber
Column = $e.InvocationInfo.OffsetInLine
}
$info | Out-String | Write-Verbose
Write-Error -Message ($info.Exception) -ErrorAction Stop
# Only here to catch a global ErrorAction overwrite
break
#endregion ErrorHandler
}
}
}
else
{
#region ErrorHandler
$paramWriteError = @{
Message = 'No directories found to cleanup'
Category = 'ObjectNotFound'
ErrorAction = 'Stop'
}
Write-Error @paramWriteError
# Only here to catch a global ErrorAction overwrite
break
#endregion ErrorHandler
}
}
#region LICENSE
<#
BSD 3-Clause License
Copyright (c) 2021, enabling Technology
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#>
#endregion LICENSE
#region DISCLAIMER
<#
DISCLAIMER:
- Use at your own risk, etc.
- This is open-source software, if you find an issue try to fix it yourself. There is no support and/or warranty in any kind
- This is a third-party Software
- The developer of this Software is NOT sponsored by or affiliated with Microsoft Corp (MSFT) or any of its subsidiaries in any way
- The Software is not supported by Microsoft Corp (MSFT)
- By using the Software, you agree to the License, Terms, and any Conditions declared and described above
- If you disagree with any of the terms, and any conditions declared: Just delete it and build your own solution
#>
#endregion DISCLAIMER

View File

@@ -0,0 +1,219 @@
#requires -Version 3.0 -RunAsAdministrator
<#
.SYNOPSIS
Cleanup some of the Exchange Logs
.DESCRIPTION
Cleanup some of the Exchange Logs
.EXAMPLE
PS C:\> .\Clear-LogFileDirectory.ps1
.NOTES
Wrapper for the Clear-LogFileDirectory function
Everything is hardcoded for this wrapper ;-)
.LINK
Clear-LogFileDirectory
#>
[CmdletBinding(ConfirmImpact = 'None')]
param ()
# Files older then 1 day are deleted
$Days = 1
# Exchange Base Directory
$ExchangeBaseDir = 'D:\Exchange Server'
# Exchange Version (Directory)
$ExchangeVersion = 'V15'
# Where to find the IIS stuff
$IISBaseDir = "$env:HOMEDRIVE\inetpub"
#region IIS
# Append the Log Stuff for the Call below
$IISLogPath = $IISBaseDir + '\logs\LogFiles\'
#endregion IIS
#region Exchange
# Combine the values
$ExchangeDirectoryPath = $ExchangeBaseDir + '\' + $ExchangeVersion
# Append the Log Stuff for the Call below
$ExchangeLoggingPath = $ExchangeDirectoryPath + '\Logging\'
$ExchangeETLTraces = $ExchangeDirectoryPath + '\Bin\Search\Ceres\Diagnostics\ETLTraces\'
$ExchangeETLLogs = $ExchangeDirectoryPath + '\Bin\Search\Ceres\Diagnostics\Logs'
#endregion Exchange
#region HelperFunction
function Clear-LogFileDirectory
{
<#
.SYNOPSIS
Cleanup Files in a given Directory
.DESCRIPTION
Cleanup Files in a given Directory
.PARAMETER Path
Specifies a path, multi-value or wildcards are not yet supported!
No default so far!
.PARAMETER Days
Age of the Files to Delete.
Default is 7
.EXAMPLE
PS C:\> Clear-LogFileDirectory -Path "c:\inetpub\logs\LogFiles\"
.NOTES
Mind the Gap:
Everything within the given directory will be deleted, without any further interaction!
#>
[CmdletBinding(ConfirmImpact = 'Low',
SupportsShouldProcess)]
param
(
[Parameter(Mandatory,
ValueFromPipeline,
ValueFromPipelineByPropertyName,
HelpMessage = 'Specifies a path, multivalue or wildcards are not yet supported!')]
[ValidateNotNullOrEmpty()]
[Alias('Folder', 'TargetFolder')]
[string]
$Path,
[Parameter(ValueFromPipeline,
ValueFromPipelineByPropertyName)]
[ValidateNotNullOrEmpty()]
[Alias('Age', 'FileAge')]
[int]
$Days = 7
)
begin
{
#region Defaults
$CNT = 'Continue'
$SCT = 'SilentlyContinue'
#endregion Defaults
Write-Verbose -Message ('START: Processing of {0}' -f $Path)
}
process
{
if (Test-Path -Path $Path -ErrorAction $SCT)
{
$Now = (Get-Date)
$LastWrite = $Now.AddDays(-$Days)
#region FindAndFilterFiles
# Splat the Parameters
$paramFindAndFilterFiles = @{
Path = $Path
Recurse = $true
ErrorAction = $SCT
}
$Files = (Get-ChildItem @paramFindAndFilterFiles | Where-Object -FilterScript {
($_.Name -like '*.log') -or ($_.Name -like '*.blg') -or ($_.Name -like '*.etl')
} | Where-Object -FilterScript {
$_.lastWriteTime -le $LastWrite
} | Select-Object -ExpandProperty FullName)
#endregion FindAndFilterFiles
#region FileLooper
foreach ($File in $Files)
{
Write-Verbose -Message ('Deleting file {0}' -f $File)
try
{
if ($pscmdlet.ShouldProcess($File, 'Delete'))
{
#region DeleteFilesFound
# Splat the Parameters
$paramDeleteFilesFound = @{
Path = $File
Force = $true
Confirm = $false
ErrorAction = 'Stop'
}
$null = (Remove-Item @paramDeleteFilesFound)
#endregion DeleteFilesFound
}
}
catch
{
#region ErrorHandler
# get error record
[Management.Automation.ErrorRecord]$e = $_
# retrieve information about runtime error
$info = [PSCustomObject]@{
Exception = $e.Exception.Message
Reason = $e.CategoryInfo.Reason
Target = $e.CategoryInfo.TargetName
Script = $e.InvocationInfo.ScriptName
Line = $e.InvocationInfo.ScriptLineNumber
Column = $e.InvocationInfo.OffsetInLine
}
$info | Out-String | Write-Verbose
Write-Warning -Message ($info.Exception) -ErrorAction $CNT -WarningAction $CNT
#endregion ErrorHandler
}
}
#endregion FileLooper
}
else
{
Write-Error -Message ("The folder {0} doesn't exist! Check the folder path!" -f $Path)
}
}
end
{
Write-Verbose -Message ('DONE: Processed {0}' -f $Path)
}
}
#endregion HelperFunction
#region FunctionWrapper
Clear-LogFileDirectory -Path $IISLogPath -Days $Days
Clear-LogFileDirectory -Path $ExchangeLoggingPath -Days $Days
Clear-LogFileDirectory -Path $ExchangeETLTraces -Days $Days
Clear-LogFileDirectory -Path $ExchangeETLLogs -Days $Days
#endregion FunctionWrapper
#region LICENSE
<#
BSD 3-Clause License
Copyright (c) 2021, enabling Technology
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#>
#endregion LICENSE
#region DISCLAIMER
<#
DISCLAIMER:
- Use at your own risk, etc.
- This is open-source software, if you find an issue try to fix it yourself. There is no support and/or warranty in any kind
- This is a third-party Software
- The developer of this Software is NOT sponsored by or affiliated with Microsoft Corp (MSFT) or any of its subsidiaries in any way
- The Software is not supported by Microsoft Corp (MSFT)
- By using the Software, you agree to the License, Terms, and any Conditions declared and described above
- If you disagree with any of the terms, and any conditions declared: Just delete it and build your own solution
#>
#endregion DISCLAIMER

View File

@@ -0,0 +1,91 @@
#requires -Version 2.0
<#
.SYNOPSIS
Enabling Modern Authentication for Exchange Online
.DESCRIPTION
Enabling Modern Authentication for Exchange Online (Office 365)
.EXAMPLE
PS C:\> .\Enable-ModernAuth-Exchange.ps1
.NOTES
Works fine with Office 2013 and Office 2016 on Windows. Tested with Office 2016 on the Mac.
You must enable it on your computers (Windows and Mac) as well! It is disabled by default.
.LINK
https://blogs.technet.microsoft.com/canitpro/2015/09/11/step-by-step-setting-up-ad-fs-and-enabling-single-sign-on-to-office-365/
#>
[CmdletBinding()]
param ()
begin
{
# The Exchange Online URL
$ExoURL = 'https://outlook.office365.com/powershell-liveid/'
# Same as above, but for the German Office 365 (MCD)
#$ExoURL = 'https://outlook.office.de/powershell-liveid/'
# The Exchange Online Authentication method
$ExoAuth = 'Basic'
}
process
{
# Get the Credentials (Could also be imported if you have dem saved)
$credentials = (Get-Credential)
# Create the new session
$paramNewPSSession = @{
ConfigurationName = 'Microsoft.Exchange'
ConnectionUri = $ExoURL
Credential = $credentials
Authentication = $ExoAuth
AllowRedirection = $true
}
$ExoSession = (New-PSSession @paramNewPSSession)
# Start the Session by importing it to the PowerShell Session
$null = (Import-PSSession -Session $ExoSession)
# Enable Modern Authentication, use $false to disable it
$null = (Set-OrganizationConfig -OAuth2ClientProfileEnabled $true)
}
end
{
# Cleanup
$ExoSession = $null
}
#region LICENSE
<#
BSD 3-Clause License
Copyright (c) 2021, enabling Technology
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#>
#endregion LICENSE
#region DISCLAIMER
<#
DISCLAIMER:
- Use at your own risk, etc.
- This is open-source software, if you find an issue try to fix it yourself. There is no support and/or warranty in any kind
- This is a third-party Software
- The developer of this Software is NOT sponsored by or affiliated with Microsoft Corp (MSFT) or any of its subsidiaries in any way
- The Software is not supported by Microsoft Corp (MSFT)
- By using the Software, you agree to the License, Terms, and any Conditions declared and described above
- If you disagree with any of the terms, and any conditions declared: Just delete it and build your own solution
#>
#endregion DISCLAIMER

View File

@@ -0,0 +1,226 @@
function Get-ADExchangeServers
{
<#
.SYNOPSIS
Get all Exchange Servers from Active Directory
.DESCRIPTION
This function gets a list with info of all Exchange Servers from the Active Directory.
The Exchange tools (or a PowerShell Connection) is not needed.
That is the major difference to Get-ExchangeServer
.EXAMPLE
# Get all Exchange Servers from Active Directory
PS> Get-ADExchangeServers
path : http://nycexch01.contoso.com/powershell
server : NYCEXCH01
Fullver : Version 15.1 (Build 31034.26)
version : 15.1
Site : HQ
path : http://nycexch02.contoso.com/powershell
server : NYCEXCH02
Fullver : Version 15.1 (Build 31034.26)
version : 15.1
Site : HQ
.EXAMPLE
# No Exchange Server found! (Error)
PS> Get-ADExchangeServers
Get-ADExchangeServers : Unable to get the Exchange Information from the Active Directory!
.NOTES
Only Exchange Servers with a configured PowerShell URI will be dumped
#>
[CmdletBinding()]
[OutputType([psobject])]
param ()
begin
{
# Define some defaults
$ErrorMessage = 'Unable to get the Exchange Information from the Active Directory!'
$SC = 'SilentlyContinue'
$STP = 'Stop'
# Search configuration partition for Exchange Servers where the powershell virtual directory is enabled
try
{
$ActiveDirectoryInfo = (New-Object -TypeName adsisearcher -ArgumentList ([adsi]"LDAP://$(([adsi]'LDAP://rootdse').configurationNamingContext)"), '(&(objectclass=msExchPowerShellVirtualDirectory)(msexchinternalhostname=*))')
}
catch
{
$paramWriteError = @{
Message = $ErrorMessage
ErrorAction = $STP
WarningAction = $SC
}
Write-Error @paramWriteError
break
}
if (-not ($ActiveDirectoryInfo))
{
$paramWriteError = @{
Message = $ErrorMessage
ErrorAction = $STP
WarningAction = $SC
}
Write-Error @paramWriteError
break
}
# Create a new Object
$ADExchangeInfo = @()
}
process
{
try
{
$ActiveDirectoryInfo.findall() | Sort-Object -Descending -Property {
$_.properties.msexchversion[0]
} | ForEach-Object -Process {
# Define some defauts
$NONE = ' '
$COM = ','
if ($_.properties.msexchinternalhostname[0])
{
if ($_.properties.distinguishedname[0])
{
$SrvLdapPath = ($_.properties.distinguishedname[0] -split $COM)[3 .. 100] -join $COM
try
{
$SingleServerObject = [adsi]"LDAP://$SrvLdapPath"
}
catch
{
$SingleServerObject = $null
}
if ($SingleServerObject)
{
if ($SingleServerObject.serialnumber[0])
{
$SingleFullVersion = $SingleServerObject.serialnumber[0]
}
else
{
$SingleFullVersion = $null
}
if (($SingleServerObject.serialNumber -split $NONE)[1])
{
$SingleShortVersion = ($SingleServerObject.serialNumber -split $NONE)[1]
}
else
{
$SingleShortVersion = $null
}
if ($SingleServerObject.name[0])
{
$SingleServer = $SingleServerObject.name[0]
}
else
{
$SingleServer = $null
}
if ($SingleServerObject.msExchServerSite[0])
{
$SingleActiveDirectorySite = $SingleServerObject.msExchServerSite[0] -replace '^CN=|,.*$', ''
}
else
{
$SingleActiveDirectorySite = $null
}
}
if ($_.properties.msexchinternalhostname[0])
{
# With each virtual directory create an object to represent its details,
# if List Version or site is included, also find the server object
$paramNewObject = @{
TypeName = 'psobject'
Property = @{
path = $_.properties.msexchinternalhostname[0]
server = $SingleServer
Site = $SingleActiveDirectorySite
version = $SingleShortVersion
Fullver = $SingleFullVersion
}
}
$SingleExchangeInfo = (New-Object @paramNewObject)
# Append the Info to the Object
$ADExchangeInfo += $SingleExchangeInfo
}
}
}
}
}
catch
{
# Do nothing
Write-Verbose -Message 'Something went wrong...'
}
}
end
{
# Just dump the plain object
if ($ADExchangeInfo)
{
$ADExchangeInfo
}
else
{
$paramWriteError = @{
Message = $ErrorMessage
ErrorAction = $STP
WarningAction = $SC
}
Write-Error @paramWriteError
break
}
}
}
#region LICENSE
<#
BSD 3-Clause License
Copyright (c) 2021, enabling Technology
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#>
#endregion LICENSE
#region DISCLAIMER
<#
DISCLAIMER:
- Use at your own risk, etc.
- This is open-source software, if you find an issue try to fix it yourself. There is no support and/or warranty in any kind
- This is a third-party Software
- The developer of this Software is NOT sponsored by or affiliated with Microsoft Corp (MSFT) or any of its subsidiaries in any way
- The Software is not supported by Microsoft Corp (MSFT)
- By using the Software, you agree to the License, Terms, and any Conditions declared and described above
- If you disagree with any of the terms, and any conditions declared: Just delete it and build your own solution
#>
#endregion DISCLAIMER

View File

@@ -0,0 +1,91 @@
<#
.SYNOPSIS
Helper script to investigate a Hafnium attack
.DESCRIPTION
Helper script to investigate a Hafnium attack
.PARAMETER ReportPath
Where to save the reports
.EXAMPLE
PS C:\> .\Get-HafniumReports.ps1
.LINK
https://discuss.elastic.co/t/detection-and-response-for-hafnium-activity/266289
. LINK
https://www.msxfaq.de/exchange/update/hafnium-nachbereitung.htm
.NOTES
This does NOT replace a Anti Virus scanner and also does NOT replace the Microsoft investigation scripts!
You can use this to bring your ongoing security investigation(s) a step forward, not more but not less.
#>
[CmdletBinding(ConfirmImpact = 'None')]
param
(
[Parameter(ValueFromPipeline,
ValueFromPipelineByPropertyName)]
[ValidateNotNullOrEmpty()]
[ValidateNotNull()]
[Alias('Path')]
[string]
$ReportPath = 'C:\scripts\PowerShell\reports\Hafnium\'
)
begin
{
# Create the report directory, if needed
if (-not (Test-Path -Path $ReportPath -ErrorAction SilentlyContinue))
{
$null = (New-Item -Path $ReportPath -ItemType Directory -Force -ErrorAction Stop)
}
# Create a Timestamp
$TimeStamp = (Get-Date -Format 'yyyyMMdd_HHmmss')
}
process
{
<#
Look for commands like "Set-OABVirtualDirectory" - This is one of the known commands that the attackers used.
#>
# Get Exchange Event Logs
$null = (Get-WinEvent -LogName 'MSExchange Management' -ErrorAction SilentlyContinue | Export-Csv -Path ($ReportPath + 'MSExchangeManagement_' + $TimeStamp + '.csv') -NoTypeInformation -Force -Encoding UTF8 -ErrorAction SilentlyContinue)
<#
Look for tasks that you don't know.
"WwanSvcdcs" is one of the names that are known as related to Hafnium
Please keep in mind: Windows itself use Scheduled Tasks a lot!
#>
# Get Scheduled Task info
$null = (Get-ScheduledTask -ErrorAction SilentlyContinue | Select-Object -Property actions -ExpandProperty actions -ErrorAction SilentlyContinue | Export-Csv -Path ($ReportPath + 'ScheduledTaskInfo_' + $TimeStamp + '.csv') -NoTypeInformation -Force -Encoding UTF8 -ErrorAction SilentlyContinue)
<#
See above, and watch for tasks that are created since January 2021 that you can not identify.
Please keep in mind: Windows itself use Scheduled Tasks a lot!
#>
# TaskScheduler info
$null = (Get-WinEvent -LogName 'Microsoft-Windows-TaskScheduler/Operational' -ErrorAction SilentlyContinue | Export-Csv -Path ($ReportPath + 'TaskScheduler_' + $TimeStamp + '.csv') -NoTypeInformation -Force -Encoding UTF8 -ErrorAction SilentlyContinue)
<#
PowerShell keeps a history that will be saved into a plain ASC File. At least if the ReadLine Module is installed!
A bit work, but you can at least try to identify something strange here!
#>
# Get all History Files from PowerShell
$null = (Get-ChildItem -Path 'C:\Users' -Filter 'ConsoleHost_history.txt' -Recurse -ErrorAction SilentlyContinue -Force | ForEach-Object -Process {
$null = (Get-Content -Path $_.FullName -ErrorAction SilentlyContinue | Out-File -FilePath ($ReportPath + 'PowerShell_History_' + $TimeStamp + '.txt') -Encoding utf8 -Append -ErrorAction SilentlyContinue)
})
}
end
{
# Open the directory in the File Explorer
Invoke-Item -Path $ReportPath
}

View File

@@ -0,0 +1,29 @@
BSD 3-Clause License
Copyright (c) 2021, enabling Technology <http://enatec.io>
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@@ -0,0 +1,8 @@
# Legacy Notice
I no longer run Exchange, Skype for Business, or any other Office Server on Premises.
This is my personal [reaction](https://hochwald.net/microsoft-rolls-back-decision-to-take-away-internal-usage-rights-from-partners/) to the changes that Microsoft [announced](https://hochwald.net/microsoft-is-going-to-kill-internal-use-rights-benefit-for-partners/) for the Internal Use Rights (IUR) program. I know that they decided to reverse that changes and in theory, I could still legally use the software. However, I decided to decommission everything licensed under the terms of the Internal Use Rights (IUR) program.
In my opinion, the community always should have some benefits from the Internal Use Rights (IUR) program and/or Action Pack. Now that I decided to drop out, there will be no more such benefits.
I will _no longer maintain_ the scripts related to the Microsoft Office (on Premises) servers. They will remain here, but unmaintained. Fork the repository and maintain or extend them if you like to. The [License](https://github.com/jhochwald/PowerShell-collection/blob/master/LICENSE) allows that easily.

View File

@@ -0,0 +1,109 @@
#requires -Version 1.0
<#
.SYNOPSIS
Uninstalls the old and retired Anti-spam Agents from an Exchange Server
.DESCRIPTION
Microsoft announced that they deprecated the support for the SmartScreen Anti-spam content filters for Exchange Servers. This script uninstalls the old an retired SmartScreen Anti-spam Agents from the local Exchange Server.
This is an easy to use and light weight replacement for Uninstall-AntiSpamAgents.ps1 from the \Scripts of your Exchange Installation, it will remove just the dead parts and leave the rest as it is. Some find that it might be better to leave the rest intact.
.EXAMPLE
PS C:\> Remove-AntiSpamAgents
.NOTES
Find a suitable an solid replacement solution for your email hygiene. This could be any 3rd party solution on premise or cloud. Never use email without any good email hygiene!
If you want, you might run the Uninstall-AntiSpamAgents.ps1 from the \Scripts folder created by Setup during Exchange installation. It removes everything related to the AntiSpamAgents.
Taken from the links below.
.LINK
https://blogs.technet.microsoft.com/exchange/2016/09/01/deprecating-support-for-smartscreen-in-outlook-and-exchange/
.LINK
https://blogs.technet.microsoft.com/exchange/2017/03/23/exchange-server-edge-support-on-windows-server-2016-update/
#>
[CmdletBinding(ConfirmImpact = 'Medium',
SupportsShouldProcess = $true)]
param ()
begin
{
# Constants
$STP = 'SilentlyContinue'
# Agents to remove
$TransportAgentsToRemove = 'Content Filter Agent', 'Sender Id Agent', 'Protocol Analysis Agent'
}
process
{
# Loop over the List
foreach ($TransportAgentToRemove in $TransportAgentsToRemove)
{
# Do we have the agent we would like to remove?
if (Get-TransportAgent -Identity $TransportAgentToRemove -ErrorAction $STP -WarningAction $STP)
{
Write-Verbose -Message "Try to remove $TransportAgentToRemove"
try
{
# Do it, or dry run it?
if ($pscmdlet.ShouldProcess("$TransportAgentToRemove", 'Remove TransportAgent'))
{
# Remove it...
$paramUninstallTransportAgent = @{
Identity = $TransportAgentToRemove
ErrorAction = $STP
WarningAction = $STP
Confirm = $false
}
$null = (Uninstall-TransportAgent @paramUninstallTransportAgent)
}
}
catch
{
# Whoopsss
Write-Warning -Message "Unable to remove $TransportAgentToRemove"
}
Write-Verbose -Message "$TransportAgentToRemove was removed"
}
else
{
Write-Verbose -Message "Sorry, $TransportAgentToRemove was not found..."
}
}
}
#region LICENSE
<#
BSD 3-Clause License
Copyright (c) 2021, enabling Technology
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#>
#endregion LICENSE
#region DISCLAIMER
<#
DISCLAIMER:
- Use at your own risk, etc.
- This is open-source software, if you find an issue try to fix it yourself. There is no support and/or warranty in any kind
- This is a third-party Software
- The developer of this Software is NOT sponsored by or affiliated with Microsoft Corp (MSFT) or any of its subsidiaries in any way
- The Software is not supported by Microsoft Corp (MSFT)
- By using the Software, you agree to the License, Terms, and any Conditions declared and described above
- If you disagree with any of the terms, and any conditions declared: Just delete it and build your own solution
#>
#endregion DISCLAIMER

View File

@@ -0,0 +1,3 @@
# ExchangeNodeMaintenanceMode
New location: [https://github.com/jhochwald/ExchangeNodeMaintenanceMode](https://github.com/jhochwald/ExchangeNodeMaintenanceMode)

View File

@@ -0,0 +1,395 @@
#requires -Version 3.0
<#
.SYNOPSIS
Remove the access to Outlook for all Mailboxes in an Microsoft Office 365 Tenant
.DESCRIPTION
Remove the access to Outlook for all Mailboxes in an Microsoft Office 365 Tenant
It will remove access to OWA (Outlook Web Application), Exchange Active Sync (EAS), Outlook App and Outlook (part of the Office Suite).
.PARAMETER CredentialUser
The UPN of the admin user
.PARAMETER CredentialFile
File where the credential will be stored
Make sure that this is secured!
.PARAMETER ProxyAccessType
Determines which mechanism is used to resolve the host name. The acceptable values for this parameter are:
- IEConfig
- WinHttpConfig
- AutoDetect
- NoProxyServer
- None
The default value is None.
For information about the values of this parameter, see the description of the System.Management.Automation.Remoting.ProxyAccessTypehttp://go.microsoft.com/fwlink/?LinkId=144756 (http://go.microsoft.com/fwlink/?LinkId=144756) enumeration in the Microsoft Developer Network (MSDN) library.
.EXAMPLE
PS C:\> .\Approve-CASMailboxSettings.ps1
.EXAMPLE
PS C:\> .\Approve-CASMailboxSettings.ps1 -verbose
.NOTES
I created the script to run automated (via Windows scheduler) and it will save the password in a plain text file.
You might want to use another option to gain access to Exchange Online
Please check all values before using the script!
TODO: Run the script once before using it as scheduled task! This will create and save the credentials.
#>
[CmdletBinding(ConfirmImpact = 'None')]
param
(
[Parameter(ValueFromPipeline,
ValueFromPipelineByPropertyName)]
[ValidateNotNullOrEmpty()]
[Alias('Username', 'AdminUser')]
[string]
$CredentialUser = 'youradmin.user@contoso.com',
[Parameter(ValueFromPipeline,
ValueFromPipelineByPropertyName)]
[ValidateNotNullOrEmpty()]
[Alias('CredFile', 'SecretFile')]
[string]
$CredentialFile = ($env:LOCALAPPDATA + '\exocreds.txt'),
[Parameter(ValueFromPipeline,
ValueFromPipelineByPropertyName)]
[ValidateSet('IEConfig', 'WinHttpConfig', 'AutoDetect', 'NoProxyServer', 'None', IgnoreCase = $true)]
[ValidateNotNullOrEmpty()]
[Alias('PSSessionOptionProxy')]
[string]
$ProxyAccessType = 'None'
)
begin
{
# Admin User (Global Admin or min. Exchange Online Admin role)
if (-not ($CredentialUser))
{
$CredentialUser = 'youradmin.user@contoso.com'
}
# Where to store the password?
if (-not ($CredentialFile))
{
$CredentialFile = ($env:LOCALAPPDATA + '\exocreds.txt')
}
}
process
{
#region CredentialHandler
try
{
if (-not (Test-Path -Path $CredentialFile -ErrorAction SilentlyContinue))
{
# Do we have any credentials in memory (variable)
if (-not ($ExoCreds))
{
#
$paramGetCredential = @{
Message = 'Bitte mit einem Exchange Online Admin Benutzer anmelden'
UserName = $CredentialUser
ErrorAction = 'Stop'
}
$ExoCreds = (Get-Credential @paramGetCredential)
}
# Splat the parameters
$paramOutFile = @{
FilePath = $CredentialFile
Force = $true
Encoding = 'utf8'
ErrorAction = 'Stop'
Confirm = $false
}
# Save the file
$null = ($ExoCreds.Password | ConvertFrom-SecureString | Out-File @paramOutFile)
}
else
{
# Splat the parameters
$paramGetContent = @{
Path = $CredentialFile
Force = $true
ErrorAction = 'Stop'
}
$paramConvertToSecureString = @{
ErrorAction = 'Stop'
}
# Read and convert the file wit the password
$PwdSecureString = (Get-Content @paramGetContent | ConvertTo-SecureString @paramConvertToSecureString)
# Splat the parameters
$paramNewObject = @{
TypeName = 'System.Management.Automation.PSCredential'
ArgumentList = $CredentialUser, $PwdSecureString
}
# Create the credential object
$ExoCreds = (New-Object @paramNewObject)
# Remove the password string from memory
$PwdSecureString = $null
}
}
catch
{
#region ErrorHandler
# get error record
[Management.Automation.ErrorRecord]$e = $_
# retrieve information about runtime error
$info = [PSCustomObject]@{
Exception = $e.Exception.Message
Reason = $e.CategoryInfo.Reason
Target = $e.CategoryInfo.TargetName
Script = $e.InvocationInfo.ScriptName
Line = $e.InvocationInfo.ScriptLineNumber
Column = $e.InvocationInfo.OffsetInLine
}
$info | Out-String | Write-Verbose
Write-Error -Message ($info.Exception) -ErrorAction Stop
# Only here to catch a global ErrorAction overwrite
break
#endregion ErrorHandler
}
#endregion CredentialHandler
#region ConnectExchangeOnline
try
{
# Proxy Handling
<#
-ProxyAccessType <ProxyAccessType>
Determines which mechanism is used to resolve the host name. The acceptable values for this parameter are:
- IEConfig
- WinHttpConfig
- AutoDetect
- NoProxyServer
- None
The default value is None.
For information about the values of this parameter, see the description of the System.Management.Automation.Remoting.ProxyAccessTypehttp://go.microsoft.com/fwlink/?LinkId=144756 (http://go.microsoft.com/fwlink/?LinkId=144756) enumeration in the Microsoft Developer Network (MSDN) library.
Source:
Get-Help New-PSSessionOption -Detailed
#>
if ($ProxyAccessType)
{
# Splat the parameters
$paramNewPSSessionOption = @{
ProxyAccessType = $ProxyAccessType
ErrorAction = 'Stop'
}
# Do we need a proxy to access Office 365?
$ProxyOptions = (New-PSSessionOption @paramNewPSSessionOption)
}
# Cleanup
$ExoSession = $null
# Splat the parameters
$paramGetPSSession = @{
ErrorAction = 'SilentlyContinue'
}
$paramRemovePSSession = @{
ErrorAction = 'SilentlyContinue'
Confirm = $false
}
# Remove all existing Exchange Online Sessions
$null = (Get-PSSession @paramGetPSSession | Where-Object {
$_.ComputerName -eq 'outlook.office365.com'
} | Remove-PSSession @paramRemovePSSession)
# Splat the parameters
$paramNewPSSession = @{
ConfigurationName = 'Microsoft.Exchange'
ConnectionUri = 'https://outlook.office365.com/powershell-liveid/'
Credential = $ExoCreds
Authentication = 'Basic'
AllowRedirection = $true
ErrorAction = 'Stop'
}
# Proxy settings needed?
if ($ProxyOptions)
{
$paramNewPSSession.SessionOption = $ProxyOptions
}
# Create the session
$ExoSession = (New-PSSession @paramNewPSSession)
# Splat the parameters
$paramImportPSSession = @{
Session = $ExoSession
DisableNameChecking = $true
AllowClobber = $true
ErrorAction = 'Stop'
WarningAction = 'Continue'
}
# Create the Session
$null = (Import-PSSession @paramImportPSSession)
}
catch
{
#region ErrorHandler
# get error record
[Management.Automation.ErrorRecord]$e = $_
# retrieve information about runtime error
$info = [PSCustomObject]@{
Exception = $e.Exception.Message
Reason = $e.CategoryInfo.Reason
Target = $e.CategoryInfo.TargetName
Script = $e.InvocationInfo.ScriptName
Line = $e.InvocationInfo.ScriptLineNumber
Column = $e.InvocationInfo.OffsetInLine
}
$info | Out-String | Write-Verbose
Write-Error -Message ($info.Exception) -ErrorAction Stop
# Only here to catch a global ErrorAction overwrite
break
#endregion ErrorHandler
}
#endregion ConnectExchangeOnline
#region SetCASMailbox
try
{
# Check if the session is alive
if (-not (Get-Command -Name Get-CASMailbox))
{
# Splat the parameters
$paramWriteError = @{
Exception = 'Es scheint ein Problem mit der Exchange Online Verbindung zu geben!'
Message = 'Die erforderlichen Exchnage Online Befehle wurden nicht gefunden!'
Category = 'ResourceUnavailable'
ErrorAction = 'Stop'
}
Write-Error @paramWriteError
# Make sure we are done!
throw
}
# Splat the parameters
$paramGetCASMailbox = @{
ResultSize = 'unlimited'
Filter = {
(name -notlike 'DiscoverysearchMailbox*')
}
ErrorAction = 'Stop'
WarningAction = 'Continue'
}
$paramSetCASMailbox = @{
ActiveSyncEnabled = $false
ImapEnabled = $false
MAPIEnabled = $false
OutlookMobileEnabled = $false
OWAEnabled = $false
OWAforDevicesEnabled = $false
PopEnabled = $false
SmtpClientAuthenticationDisabled = $false
UniversalOutlookEnabled = $false
Confirm = $false
ErrorAction = 'Continue'
WarningAction = 'Continue'
}
# Remove the outlook access from all mailboxes
$null = (Get-CASMailbox @paramGetCASMailbox | Set-CASMailbox @paramSetCASMailbox)
}
catch
{
#region ErrorHandler
# get error record
[Management.Automation.ErrorRecord]$e = $_
# retrieve information about runtime error
$info = [PSCustomObject]@{
Exception = $e.Exception.Message
Reason = $e.CategoryInfo.Reason
Target = $e.CategoryInfo.TargetName
Script = $e.InvocationInfo.ScriptName
Line = $e.InvocationInfo.ScriptLineNumber
Column = $e.InvocationInfo.OffsetInLine
}
$info | Out-String | Write-Verbose
Write-Error -Message ($info.Exception) -ErrorAction Stop
# Only here to catch a global ErrorAction overwrite
break
#endregion ErrorHandler
}
#endregion SetCASMailbox
}
end
{
# Cleanup
$ExoSession = $null
# Splat the parameters
$paramGetPSSession = @{
ErrorAction = 'SilentlyContinue'
}
$paramRemovePSSession = @{
ErrorAction = 'SilentlyContinue'
Confirm = $false
}
# Remove all existing Exchange Online Sessions
$null = (Get-PSSession @paramGetPSSession | Where-Object {
$_.ComputerName -eq 'outlook.office365.com'
} | Remove-PSSession @paramRemovePSSession)
}
#region LICENSE
<#
BSD 3-Clause License
Copyright (c) 2021, enabling Technology
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#>
#endregion LICENSE
#region DISCLAIMER
<#
DISCLAIMER:
- Use at your own risk, etc.
- This is open-source software, if you find an issue try to fix it yourself. There is no support and/or warranty in any kind
- This is a third-party Software
- The developer of this Software is NOT sponsored by or affiliated with Microsoft Corp (MSFT) or any of its subsidiaries in any way
- The Software is not supported by Microsoft Corp (MSFT)
- By using the Software, you agree to the License, Terms, and any Conditions declared and described above
- If you disagree with any of the terms, and any conditions declared: Just delete it and build your own solution
#>
#endregion DISCLAIMER

View File

@@ -0,0 +1,617 @@
function Export-DistributionGroup2Cloud
{
<#
.SYNOPSIS
Function to convert/migrate on-premises Exchange distribution group to a Cloud (Exchange Online) distribution group
.DESCRIPTION
Copies attributes of a synchronized group to a placeholder group and CSV file.
After initial export of group attributes, the on-premises group can have the attribute "AdminDescription" set to "Group_NoSync" which will stop it from be synchronized.
The "-Finalize" switch can then be used to write the addresses to the new group and convert the name. The final group will be a cloud group with the same attributes as the previous but with the additional ability of being able to be "self-managed".
Once the contents of the new group are validated, the on-premises group can be deleted.
.PARAMETER Group
Name of group to recreate.
.PARAMETER CreatePlaceHolder
Create placeholder DistributionGroup wit ha given name.
.PARAMETER Finalize
Convert a given placeholder group to final DistributionGroup.
.PARAMETER ExportDirectory
Export Directory for internal CSV handling.
.EXAMPLE
PS> Export-DistributionGroup2Cloud -Group "DL-Marketing" -CreatePlaceHolder
Create the Placeholder for the distribution group "DL-Marketing"
.EXAMPLE
PS> Export-DistributionGroup2Cloud -Group "DL-Marketing" -Finalize
Transform the Placeholder for the distribution group "DL-Marketing" to the real distribution group in the cloud
.NOTES
This function is based on the Recreate-DistributionGroup.ps1 script of Joe Palarchio
License: BSD 3-Clause
.LINK
https://gallery.technet.microsoft.com/PowerShell-Script-to-Move-5c3cd668
.LINK
http://blogs.perficient.com/microsoft/?p=32092
#>
[CmdletBinding(ConfirmImpact = 'Low')]
param
(
[Parameter(Mandatory,
HelpMessage = 'Name of group to recreate.')]
[string]
$Group,
[switch]
$CreatePlaceHolder,
[switch]
$Finalize,
[ValidateNotNullOrEmpty()]
[string]
$ExportDirectory = 'C:\scripts\PowerShell\exports\ExportedAddresses\'
)
begin
{
# Defaults
$SCN = 'SilentlyContinue'
$CNT = 'Continue'
$STP = 'Stop'
}
process
{
If ($CreatePlaceHolder.IsPresent)
{
# Create the Placeholder
If (((Get-DistributionGroup -Identity $Group -ErrorAction $SCN).IsValid) -eq $True)
{
# Splat to make it more human readable
$paramGetDistributionGroup = @{
Identity = $Group
ErrorAction = $STP
WarningAction = $CNT
}
try
{
$OldDG = (Get-DistributionGroup @paramGetDistributionGroup)
}
catch
{
$line = ($_.InvocationInfo.ScriptLineNumber)
# Dump the Info
Write-Warning -Message ('Error was in Line {0}' -f $line)
# Dump the Error catched
Write-Error -Message $_ -ErrorAction $STP
# Something that should never be reached
break
}
try
{
[IO.Path]::GetInvalidFileNameChars() | ForEach-Object -Process {
$Group = $Group.Replace($_, '_')
}
}
catch
{
$line = ($_.InvocationInfo.ScriptLineNumber)
# Dump the Info
Write-Warning -Message ('Error was in Line {0}' -f $line)
# Dump the Error catched
Write-Error -Message $_ -ErrorAction $STP
# Something that should never be reached
break
}
$OldName = [string]$OldDG.Name
$OldDisplayName = [string]$OldDG.DisplayName
$OldPrimarySmtpAddress = [string]$OldDG.PrimarySmtpAddress
$OldAlias = [string]$OldDG.Alias
# Splat to make it more human readable
$paramGetDistributionGroupMember = @{
Identity = $OldDG.Name
ErrorAction = $STP
WarningAction = $CNT
}
try
{
$OldMembers = ((Get-DistributionGroupMember @paramGetDistributionGroupMember).Name)
}
catch
{
$line = ($_.InvocationInfo.ScriptLineNumber)
# Dump the Info
Write-Warning -Message ('Error was in Line {0}' -f $line)
# Dump the Error catched
Write-Error -Message $_ -ErrorAction $STP
# Something that should never be reached
break
}
If (!(Test-Path -Path $ExportDirectory -ErrorAction $SCN -WarningAction $CNT))
{
Write-Verbose -Message (' Creating Directory: {0}' -f $ExportDirectory)
# Splat to make it more human readable
$paramNewItem = @{
ItemType = 'directory'
Path = $ExportDirectory
Force = $True
Confirm = $False
ErrorAction = $STP
WarningAction = $CNT
}
try
{
$null = (New-Item @paramNewItem)
}
catch
{
$line = ($_.InvocationInfo.ScriptLineNumber)
# Dump the Info
Write-Warning -Message ('Error was in Line {0}' -f $line)
# Dump the Error catched
Write-Error -Message $_ -ErrorAction $STP
# Something that should never be reached
break
}
}
# Define variables - mostly for future use
$ExportDirectoryGroupCsv = $ExportDirectory + '\' + $Group + '.csv'
try
{
# TODO: Refactor in future version
'EmailAddress' > $ExportDirectoryGroupCsv
$OldDG.EmailAddresses >> $ExportDirectoryGroupCsv
'x500:' + $OldDG.LegacyExchangeDN >> $ExportDirectoryGroupCsv
}
catch
{
$line = ($_.InvocationInfo.ScriptLineNumber)
# Dump the Info
Write-Warning -Message ('Error was in Line {0}' -f $line)
# Dump the Error catched
Write-Error -Message $_ -ErrorAction $STP
# Something that should never be reached
break
}
# Define variables - mostly for future use
$NewDistributionGroupName = 'Cloud- ' + $OldName
$NewDistributionGroupAlias = 'Cloud-' + $OldAlias
$NewDistributionGroupDisplayName = 'Cloud-' + $OldDisplayName
$NewDistributionGroupPrimarySmtpAddress = 'Cloud-' + $OldPrimarySmtpAddress
# TODO: Replace with Write-Verbose in future version of the function
Write-Output -InputObject (' Creating Group: {0}' -f $NewDistributionGroupDisplayName)
# Splat to make it more human readable
$paramNewDistributionGroup = @{
Name = $NewDistributionGroupName
Alias = $NewDistributionGroupAlias
DisplayName = $NewDistributionGroupDisplayName
ManagedBy = $OldDG.ManagedBy
Members = $OldMembers
PrimarySmtpAddress = $NewDistributionGroupPrimarySmtpAddress
ErrorAction = $STP
WarningAction = $CNT
}
try
{
$null = (New-DistributionGroup @paramNewDistributionGroup)
}
catch
{
$line = ($_.InvocationInfo.ScriptLineNumber)
# Dump the Info
Write-Warning -Message ('Error was in Line {0}' -f $line)
# Dump the Error catched
Write-Error -Message $_ -ErrorAction $STP
# Something that should never be reached
break
}
# Wait for 3 seconds
$null = (Start-Sleep -Seconds 3)
# Define variables - mostly for future use
$SetDistributionGroupIdentity = 'Cloud-' + $OldName
$SetDistributionGroupDisplayName = 'Cloud-' + $OldDisplayName
# TODO: Replace with Write-Verbose in future version of the function
Write-Output -InputObject (' Setting Values For: {0}' -f $SetDistributionGroupDisplayName)
# Splat to make it more human readable
$paramSetDistributionGroup = @{
Identity = $SetDistributionGroupIdentity
AcceptMessagesOnlyFromSendersOrMembers = $OldDG.AcceptMessagesOnlyFromSendersOrMembers
RejectMessagesFromSendersOrMembers = $OldDG.RejectMessagesFromSendersOrMembers
ErrorAction = $STP
WarningAction = $CNT
}
try
{
$null = (Set-DistributionGroup @paramSetDistributionGroup)
}
catch
{
$line = ($_.InvocationInfo.ScriptLineNumber)
# Dump the Info
Write-Warning -Message ('Error was in Line {0}' -f $line)
# Dump the Error catched
Write-Error -Message $_ -ErrorAction $STP
# Something that should never be reached
break
}
# Define variables - mostly for future use
$SetDistributionGroupIdentity = 'Cloud-' + $OldName
# Splat to make it more human readable
$paramSetDistributionGroup = @{
Identity = $SetDistributionGroupIdentity
AcceptMessagesOnlyFrom = $OldDG.AcceptMessagesOnlyFrom
AcceptMessagesOnlyFromDLMembers = $OldDG.AcceptMessagesOnlyFromDLMembers
BypassModerationFromSendersOrMembers = $OldDG.BypassModerationFromSendersOrMembers
BypassNestedModerationEnabled = $OldDG.BypassNestedModerationEnabled
CustomAttribute1 = $OldDG.CustomAttribute1
CustomAttribute2 = $OldDG.CustomAttribute2
CustomAttribute3 = $OldDG.CustomAttribute3
CustomAttribute4 = $OldDG.CustomAttribute4
CustomAttribute5 = $OldDG.CustomAttribute5
CustomAttribute6 = $OldDG.CustomAttribute6
CustomAttribute7 = $OldDG.CustomAttribute7
CustomAttribute8 = $OldDG.CustomAttribute8
CustomAttribute9 = $OldDG.CustomAttribute9
CustomAttribute10 = $OldDG.CustomAttribute10
CustomAttribute11 = $OldDG.CustomAttribute11
CustomAttribute12 = $OldDG.CustomAttribute12
CustomAttribute13 = $OldDG.CustomAttribute13
CustomAttribute14 = $OldDG.CustomAttribute14
CustomAttribute15 = $OldDG.CustomAttribute15
ExtensionCustomAttribute1 = $OldDG.ExtensionCustomAttribute1
ExtensionCustomAttribute2 = $OldDG.ExtensionCustomAttribute2
ExtensionCustomAttribute3 = $OldDG.ExtensionCustomAttribute3
ExtensionCustomAttribute4 = $OldDG.ExtensionCustomAttribute4
ExtensionCustomAttribute5 = $OldDG.ExtensionCustomAttribute5
GrantSendOnBehalfTo = $OldDG.GrantSendOnBehalfTo
HiddenFromAddressListsEnabled = $True
MailTip = $OldDG.MailTip
MailTipTranslations = $OldDG.MailTipTranslations
MemberDepartRestriction = $OldDG.MemberDepartRestriction
MemberJoinRestriction = $OldDG.MemberJoinRestriction
ModeratedBy = $OldDG.ModeratedBy
ModerationEnabled = $OldDG.ModerationEnabled
RejectMessagesFrom = $OldDG.RejectMessagesFrom
RejectMessagesFromDLMembers = $OldDG.RejectMessagesFromDLMembers
ReportToManagerEnabled = $OldDG.ReportToManagerEnabled
ReportToOriginatorEnabled = $OldDG.ReportToOriginatorEnabled
RequireSenderAuthenticationEnabled = $OldDG.RequireSenderAuthenticationEnabled
SendModerationNotifications = $OldDG.SendModerationNotifications
SendOofMessageToOriginatorEnabled = $OldDG.SendOofMessageToOriginatorEnabled
BypassSecurityGroupManagerCheck = $True
ErrorAction = $STP
WarningAction = $CNT
}
try
{
$null = (Set-DistributionGroup @paramSetDistributionGroup)
}
catch
{
$line = ($_.InvocationInfo.ScriptLineNumber)
# Dump the Info
Write-Warning -Message ('Error was in Line {0}' -f $line)
# Dump the Error catched
Write-Error -Message $_ -ErrorAction $STP
# Something that should never be reached
break
}
}
Else
{
Write-Error -Message ('The distribution group {0} was not found' -f $Group) -ErrorAction $CNT
}
}
ElseIf ($Finalize.IsPresent)
{
# Do the final steps
# Define variables - mostly for future use
$GetDistributionGroupIdentity = 'Cloud-' + $Group
# Splat to make it more human readable
$paramGetDistributionGroup = @{
Identity = $GetDistributionGroupIdentity
ErrorAction = $STP
WarningAction = $CNT
}
try
{
$TempDG = (Get-DistributionGroup @paramGetDistributionGroup)
}
catch
{
$line = ($_.InvocationInfo.ScriptLineNumber)
# Dump the Info
Write-Warning -Message ('Error was in Line {0}' -f $line)
# Dump the Error catched
Write-Error -Message $_ -ErrorAction $STP
# Something that should never be reached
break
}
$TempPrimarySmtpAddress = $TempDG.PrimarySmtpAddress
try
{
[IO.Path]::GetInvalidFileNameChars() | ForEach-Object -Process {
$Group = $Group.Replace($_, '_')
}
}
catch
{
$line = ($_.InvocationInfo.ScriptLineNumber)
# Dump the Info
Write-Warning -Message ('Error was in Line {0}' -f $line)
# Dump the Error catched
Write-Error -Message $_ -ErrorAction $STP
# Something that should never be reached
break
}
$OldAddressesPatch = $ExportDirectory + '\' + $Group + '.csv'
# Splat to make it more human readable
$paramImportCsv = @{
Path = $OldAddressesPatch
ErrorAction = $STP
WarningAction = $CNT
}
try
{
$OldAddresses = @(Import-Csv @paramImportCsv)
}
catch
{
$line = ($_.InvocationInfo.ScriptLineNumber)
# Dump the Info
Write-Warning -Message ('Error was in Line {0}' -f $line)
# Dump the Error catched
Write-Error -Message $_ -ErrorAction $STP
# Something that should never be reached
break
}
try
{
$NewAddresses = $OldAddresses | ForEach-Object -Process {
$_.EmailAddress.Replace('X500', 'x500')
}
}
catch
{
$line = ($_.InvocationInfo.ScriptLineNumber)
# Dump the Info
Write-Warning -Message ('Error was in Line {0}' -f $line)
# Dump the Error catched
Write-Error -Message $_ -ErrorAction $STP
# Something that should never be reached
break
}
$NewDGName = $TempDG.Name.Replace('Cloud-', '')
$NewDGDisplayName = $TempDG.DisplayName.Replace('Cloud-', '')
$NewDGAlias = $TempDG.Alias.Replace('Cloud-', '')
try
{
$NewPrimarySmtpAddress = ($NewAddresses | Where-Object -FilterScript {
$_ -clike 'SMTP:*'
}).Replace('SMTP:', '')
}
catch
{
$line = ($_.InvocationInfo.ScriptLineNumber)
# Dump the Info
Write-Warning -Message ('Error was in Line {0}' -f $line)
# Dump the Error catched
Write-Error -Message $_ -ErrorAction $STP
# Something that should never be reached
break
}
# Splat to make it more human readable
$paramSetDistributionGroup = @{
Identity = $TempDG.Name
Name = $NewDGName
Alias = $NewDGAlias
DisplayName = $NewDGDisplayName
PrimarySmtpAddress = $NewPrimarySmtpAddress
HiddenFromAddressListsEnabled = $False
BypassSecurityGroupManagerCheck = $True
ErrorAction = $STP
WarningAction = $CNT
}
try
{
$null = (Set-DistributionGroup @paramSetDistributionGroup)
}
catch
{
$line = ($_.InvocationInfo.ScriptLineNumber)
# Dump the Info
Write-Warning -Message ('Error was in Line {0}' -f $line)
# Dump the Error catched
Write-Error -Message $_ -ErrorAction $STP
# Something that should never be reached
break
}
$paramSetDistributionGroup = @{
Identity = $NewDGName
EmailAddresses = @{
Add = $NewAddresses
}
BypassSecurityGroupManagerCheck = $True
ErrorAction = $STP
WarningAction = $CNT
}
try
{
$null = (Set-DistributionGroup @paramSetDistributionGroup)
}
catch
{
$line = ($_.InvocationInfo.ScriptLineNumber)
# Dump the Info
Write-Warning -Message ('Error was in Line {0}' -f $line)
# Dump the Error catched
Write-Error -Message $_ -ErrorAction $STP
# Something that should never be reached
break
}
# Splat to make it more human readable
$paramSetDistributionGroup = @{
Identity = $NewDGName
EmailAddresses = @{
Remove = $TempPrimarySmtpAddress
}
BypassSecurityGroupManagerCheck = $True
ErrorAction = $STP
WarningAction = $CNT
}
try
{
$null = (Set-DistributionGroup @paramSetDistributionGroup)
}
catch
{
$line = ($_.InvocationInfo.ScriptLineNumber)
# Dump the Info
Write-Warning -Message ('Error was in Line {0}' -f $line)
# Dump the Error catched
Write-Error -Message $_ -ErrorAction $STP
# Something that should never be reached
break
}
}
Else
{
Write-Error -Message " ERROR: No options selected, please use '-CreatePlaceHolder' or '-Finalize'" -ErrorAction $STP
# Something that should never be reached
break
}
}
end
{
<#
From the original Script Author
Name: Recreate-DistributionGroup.ps1
Version: 1.0
Description: Copies attributes of a synchronized group to a placeholder group and CSV file.
After initial export of group attributes, the on-premises group can have the attribute "AdminDescription" set to "Group_NoSync" which will stop it from be synchronized.
The "-Finalize" switch can then be used to write the addresses to the new group and convert the name. The final group will be a cloud group with the same attributes as the previous but with the additional ability of being able to be "self-managed".
Once the contents of the new group are validated, the on-premises group can be deleted.
Requires: Remote PowerShell Connection to Exchange Online
Author: Joe Palarchio
Usage: Additional information on the usage of this script can found at the following blog post: http://blogs.perficient.com/microsoft/?p=32092
Disclaimer: This script is provided AS IS without any support. Please test in a lab environment prior to production use.
#>
}
}
#region LICENSE
<#
BSD 3-Clause License
Copyright (c) 2021, enabling Technology
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#>
#endregion LICENSE
#region DISCLAIMER
<#
DISCLAIMER:
- Use at your own risk, etc.
- This is open-source software, if you find an issue try to fix it yourself. There is no support and/or warranty in any kind
- This is a third-party Software
- The developer of this Software is NOT sponsored by or affiliated with Microsoft Corp (MSFT) or any of its subsidiaries in any way
- The Software is not supported by Microsoft Corp (MSFT)
- By using the Software, you agree to the License, Terms, and any Conditions declared and described above
- If you disagree with any of the terms, and any conditions declared: Just delete it and build your own solution
#>
#endregion DISCLAIMER

View File

@@ -0,0 +1,275 @@
#requires -Version 3.0 -Modules ExchangeOnlineManagement
<#
.SYNOPSIS
Get a basic report of Mobile Devices
.DESCRIPTION
Get a basic report of Mobile Devices connected to the Microsoft 365 Tenant
.EXAMPLE
PS C:\> .\Get-MobileDeviceReporting.ps1
.LINK
Connect-ExchangeOnline
.LINK
Get-MobileDevice
.LINK
Get-MobileDeviceStatistics
.NOTES
Nothing fancy! Only a basic report as CSV
#>
[CmdletBinding(ConfirmImpact = 'None')]
param ()
begin
{
# Cleanup
$Stats = $null
$DeviceStats = $null
$Report = $null
$MobileDeviceList = $null
# Garbage Collection
[GC]::Collect()
try
{
$paramConnectExchangeOnline = @{
ShowBanner = $true
BypassMailboxAnchoring = $true
ExchangeEnvironmentName = 'O365Default'
ErrorAction = 'SilentlyContinue'
}
$null = (Connect-ExchangeOnline @paramConnectExchangeOnline)
}
catch
{
#region ErrorHandler
# get error record
[Management.Automation.ErrorRecord]$e = $_
# retrieve information about runtime error
$info = [PSCustomObject]@{
Exception = $e.Exception.Message
Reason = $e.CategoryInfo.Reason
Target = $e.CategoryInfo.TargetName
Script = $e.InvocationInfo.ScriptName
Line = $e.InvocationInfo.ScriptLineNumber
Column = $e.InvocationInfo.OffsetInLine
}
# output information. Post-process collected info, and log info (optional)
$info | Out-String | Write-Verbose
$paramWriteError = @{
Message = $e.Exception.Message
ErrorAction = 'Stop'
Exception = $e.Exception
TargetObject = $e.CategoryInfo.TargetName
}
Write-Error @paramWriteError
# Only here to catch a global ErrorAction overwrite
exit 1
#endregion ErrorHandler
}
# Create new object
$Report = @()
}
process
{
# Get all mobile devices in the Microsoft 365 tenant
<#
Option: -ActiveSync
Description: The ActiveSync switch filters the results by Exchange ActiveSync devices.
Source: https://docs.microsoft.com/en-us/powershell/module/exchange/get-mobiledevice?view=exchange-ps
#>
try
{
$paramGetMobileDevice = @{
ResultSize = 'unlimited'
ErrorAction = 'Stop'
}
$MobileDeviceList = (Get-MobileDevice @paramGetMobileDevice)
}
catch
{
#region ErrorHandler
# get error record
[Management.Automation.ErrorRecord]$e = $_
# retrieve information about runtime error
$info = [PSCustomObject]@{
Exception = $e.Exception.Message
Reason = $e.CategoryInfo.Reason
Target = $e.CategoryInfo.TargetName
Script = $e.InvocationInfo.ScriptName
Line = $e.InvocationInfo.ScriptLineNumber
Column = $e.InvocationInfo.OffsetInLine
}
# output information. Post-process collected info, and log info (optional)
$info | Out-String | Write-Verbose
$paramWriteError = @{
Message = $e.Exception.Message
ErrorAction = 'Stop'
Exception = $e.Exception
TargetObject = $e.CategoryInfo.TargetName
}
Write-Error @paramWriteError
# Only here to catch a global ErrorAction overwrite
exit 1
#endregion ErrorHandler
}
# Loop over the List
foreach ($Device in $MobileDeviceList)
{
$Stats = $null
$DeviceStats = $null
try
{
$paramGetMobileDeviceStatistics = @{
Identity = $Device.Guid.toString()
ErrorAction = 'Stop'
}
$Stats = (Get-MobileDeviceStatistics @paramGetMobileDeviceStatistics)
$DeviceStats = [PSCustomObject]@{
Identity = $Device.Identity -replace '\\.+'
DeviceType = $Device.DeviceType
DeviceOS = $Device.DeviceOS
DeviceUserAgent = $Stats.DeviceUserAgent
DeviceModel = $Stats.DeviceModel
ClientType = $Stats.ClientType
FirstSyncTime = $Stats.FirstSyncTime
LastSuccessSync = $Stats.LastSuccessSync
LastSyncAttemptTime = $Stats.LastSyncAttemptTime
LastPolicyUpdateTime = $Stats.LastPolicyUpdateTime
LastPingHeartbeat = $Stats.LastPingHeartbeat
}
$Report += $DeviceStats
}
catch
{
#region ErrorHandler
# get error record
[Management.Automation.ErrorRecord]$e = $_
# retrieve information about runtime error
$info = [PSCustomObject]@{
Exception = $e.Exception.Message
Reason = $e.CategoryInfo.Reason
Target = $e.CategoryInfo.TargetName
Script = $e.InvocationInfo.ScriptName
Line = $e.InvocationInfo.ScriptLineNumber
Column = $e.InvocationInfo.OffsetInLine
}
# output information. Post-process collected info, and log info (optional)
$info | Out-String | Write-Verbose
Write-Warning -Message $info.Exception
#endregion ErrorHandler
}
}
# Create a Timestamp (check if this is OK for you)
$TimeStamp = (Get-Date -Format yyyyMMdd_HHmmss)
# Export the CSV Report
try
{
$paramExportCsv = @{
Path = ('.\MobileDeviceReport' + $TimeStamp + '.csv')
Force = $true
Encoding = 'UTF8'
Delimiter = ';'
NoTypeInformation = $true
ErrorAction = 'Stop'
}
($Report | Export-Csv @paramExportCsv)
}
catch
{
#region ErrorHandler
# get error record
[Management.Automation.ErrorRecord]$e = $_
# retrieve information about runtime error
$info = [PSCustomObject]@{
Exception = $e.Exception.Message
Reason = $e.CategoryInfo.Reason
Target = $e.CategoryInfo.TargetName
Script = $e.InvocationInfo.ScriptName
Line = $e.InvocationInfo.ScriptLineNumber
Column = $e.InvocationInfo.OffsetInLine
}
# output information. Post-process collected info, and log info (optional)
$info | Out-String | Write-Verbose
$paramWriteError = @{
Message = $e.Exception.Message
ErrorAction = 'Stop'
Exception = $e.Exception
TargetObject = $e.CategoryInfo.TargetName
}
Write-Error @paramWriteError
#endregion ErrorHandler
}
finally
{
# Disconnect from Exchange Online
$paramDisconnectExchangeOnline = @{
Confirm = $false
ErrorAction = 'SilentlyContinue'
}
$null = (Disconnect-ExchangeOnline @paramDisconnectExchangeOnline)
# Cleanup
$Stats = $null
$DeviceStats = $null
$Report = $null
$MobileDeviceList = $null
# Garbage Collection
[GC]::Collect()
}
}
#region LICENSE
<#
BSD 3-Clause License
Copyright (c) 2021, enabling Technology
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#>
#endregion LICENSE
#region DISCLAIMER
<#
DISCLAIMER:
- Use at your own risk, etc.
- This is open-source software, if you find an issue try to fix it yourself. There is no support and/or warranty in any kind
- This is a third-party Software
- The developer of this Software is NOT sponsored by or affiliated with Microsoft Corp (MSFT) or any of its subsidiaries in any way
- The Software is not supported by Microsoft Corp (MSFT)
- By using the Software, you agree to the License, Terms, and any Conditions declared and described above
- If you disagree with any of the terms, and any conditions declared: Just delete it and build your own solution
#>
#endregion DISCLAIMER

View File

@@ -0,0 +1,393 @@
function Get-enMailboxFolderPermissionReport
{
<#
.SYNOPSIS
Get a detailed mailbox folder permission report
.DESCRIPTION
Get a detailed mailbox folder permission report and exports this report to a given CSV file.
You can select only user-mailboxes, only shared-mailboxes or both for the reporting.
.PARAMETER Identity
The Identity parameter specifies the mailbox that you want to view.
You can use any value that uniquely identifies the mailbox.
Default is * (all)
.PARAMETER MailboxType
The type is the value for the regular RecipientTypeDetails.
The acceptable values for this parameter are:
- UserMailbox
- User
- SharedMailbox
- Shared
- All
The Default is ALL
.PARAMETER ResultSize
The ResultSize parameter specifies the maximum number of results to return.
If you want to return all requests that match the query, use unlimited for the value of this parameter.
The default value is unlimited.
.PARAMETER Path
Specifies the path to the CSV output file.
The default is 'C:\scripts\PowerShell\Reports\MailboxFolderPermissionReport.csv'
.PARAMETER Encoding
Specifies the encoding for the exported CSV file.
The acceptable values for this parameter are:
- Unicode
- UTF7
- UTF8
- ASCII
- UTF32
- BigEndianUnicode
- Default
- OEM
Default is UTF8
.EXAMPLE
PS C:\> Get-enMailboxFolderPermissionReport
Get a detailed mailbox folder permission report
.NOTES
Developed and tested with Exchange Online, it should work with on Premises Exchange 2010/2010/2016/2019
This is open-source software, if you find an issue try to fix it yourself.
There is no support and/or warranty in any kind
.LINK
http://www.enatec.io
.LINK
Get-Mailbox
.LINK
Get-MailboxFolderStatistics
.LINK
Get-MailboxFolderPermission
.LINK
Export-Csv
#>
[CmdletBinding(ConfirmImpact = 'None')]
param
(
[Parameter(ValueFromPipeline = $true,
ValueFromPipelineByPropertyName = $true)]
[AllowEmptyString()]
[AllowEmptyCollection()]
[Alias('Mailbox', 'MailboxID', 'MailboxIdentity')]
[string]
$Identity = '*',
[Parameter(ValueFromPipeline = $true,
ValueFromPipelineByPropertyName = $true)]
[ValidateSet('UserMailbox', 'User', 'SharedMailbox', 'Shared', 'All', IgnoreCase = $true)]
[string]
$MailboxType = 'All',
[Parameter(ValueFromPipeline = $true,
ValueFromPipelineByPropertyName = $true)]
[AllowEmptyCollection()]
[AllowEmptyString()]
[Alias('MailboxResultSize')]
[string]
$ResultSize = 'Unlimited',
[Parameter(ValueFromPipeline = $true,
ValueFromPipelineByPropertyName = $true)]
[AllowEmptyCollection()]
[AllowEmptyString()]
[Alias('CsvReport', 'CsvFile')]
[string]
$Path = 'C:\scripts\PowerShell\Reports\MailboxFolderPermissionReport.csv',
[Parameter(ValueFromPipeline = $true,
ValueFromPipelineByPropertyName = $true)]
[ValidateSet('Unicode', 'UTF7', 'UTF8', 'ASCII', 'UTF32', 'BigEndianUnicode', 'Default', 'OEM', IgnoreCase = $true)]
[AllowEmptyCollection()]
[AllowEmptyString()]
[Alias('CsvEncoding')]
[string]
$Encoding = 'UTF8'
)
begin
{
#region Cleanup
$MailboxPermissionReport = $null
$AllMailboxes = $null
$MailboxCount = $null
$MailboxFolderPermission = $null
$ProgressStatus = $null
#endregion Cleanup
#region Defaults
$SCT = 'SilentlyContinue'
$CNT = 'Continue'
if (-not ($Identity))
{
$Identity = '*'
}
if (-not ($MailboxType))
{
$MailboxType = 'All'
}
if (-not ($ResultSize))
{
$ResultSize = 'Unlimited'
}
if (-not ($Path))
{
$Path = 'C:\scripts\PowerShell\Reports\MailboxFolderPermissionReport.csv'
}
if (-not ($Encoding))
{
$Encoding = 'UTF8'
}
#endregion Defaults
#region MailboxType
Write-Verbose -Message 'Get the mailboxes'
#region paramGetMailbox
$paramGetMailbox = @{
Identity = $Identity
ResultSize = $ResultSize
ErrorAction = $SCT
WarningAction = $CNT
}
#endregion paramGetMailbox
#region MailboxTypeSwitch
switch ($MailboxType)
{
UserMailbox
{
$paramWhereObject = @{
FilterScript = {
$_.RecipientTypeDetails -eq 'UserMailbox'
}
}
}
User
{
$paramWhereObject = @{
FilterScript = {
$_.RecipientTypeDetails -eq 'UserMailbox'
}
}
}
SharedMailbox
{
$paramWhereObject = @{
FilterScript = {
$_.RecipientTypeDetails -eq 'SharedMailbox'
}
}
}
Shared
{
$paramWhereObject = @{
FilterScript = {
$_.RecipientTypeDetails -eq 'SharedMailbox'
}
}
}
All
{
$paramWhereObject = @{
FilterScript = {
$_.RecipientTypeDetails -eq 'UserMailbox' -or $_.RecipientTypeDetails -eq 'SharedMailbox'
}
}
}
default
{
$paramWhereObject = @{
FilterScript = {
$_.RecipientTypeDetails -eq 'UserMailbox' -or $_.RecipientTypeDetails -eq 'SharedMailbox'
}
}
}
}
#endregion MailboxTypeSwitch
#region GetAllMailboxes
$AllMailboxes = (Get-Mailbox @paramGetMailbox | Where-Object @paramWhereObject | Sort-Object)
#endregion GetAllMailboxes
#endregion MailboxType
}
process
{
if ($AllMailboxes)
{
# Create a new object for the report
$MailboxPermissionReport = @()
# Create a counter for Write-Progress
$MailboxCounter = ($AllMailboxes | Measure-Object).Count
# Set the start counter for Write-Progress to 1
$MailboxCount = 1
#region MailboxLoop
Write-Verbose -Message 'Process all mailboxes'
ForEach ($SingleMailbox in $AllMailboxes)
{
# Update Write-Progress
$ProgressActivity = ('Working on Mailbox {0} of {1} ({2})' -f $MailboxCount, $MailboxCounter, $SingleMailbox.UserPrincipalName)
$ProgressStatus = ('Getting folders for mailbox: {0} ({1})' -f $SingleMailbox.DisplayName, $SingleMailbox.UserPrincipalName)
Write-Verbose -Message $ProgressStatus
$paramWriteProgress = @{
Status = $ProgressStatus
Activity = $ProgressActivity
PercentComplete = (($MailboxCount/$MailboxCounter) * 100)
}
Write-Progress @paramWriteProgress
# Get all folder for the mailbox
$AllFolders = ($SingleMailbox | Get-MailboxFolderStatistics -FolderScope All | ForEach-Object -Process {
$_.folderpath
} | ForEach-Object -Process {
$_.replace('/', '\')
})
ForEach ($SingleFolder in $AllFolders)
{
# Update Write-Progress
$ProgressStatus = ('Get permissions for {0}' -f ($SingleMailbox.UserPrincipalName + ':' + $SingleFolder))
Write-Verbose -Message $ProgressStatus
$paramWriteProgress = @{
Status = $ProgressStatus
Activity = $ProgressActivity
PercentComplete = (($MailboxCount/$MailboxCounter) * 100)
}
Write-Progress @paramWriteProgress
# Get mailbox folder permissions with Get-MailboxFolderPermission
$MailboxFolderPermission = $null
$paramGetMailboxFolderPermission = @{
Identity = ($SingleMailbox.Alias + ':' + $SingleFolder)
ErrorAction = $SCT
}
$MailboxFolderPermission = (Get-MailboxFolderPermission @paramGetMailboxFolderPermission)
# store results in variable
$MailboxPermissionReport += $MailboxFolderPermission | Where-Object -FilterScript {
$_.User -notlike 'Default' -and $_.User -notlike 'Anonymous' -and $_.AccessRights -notlike 'None' -and $_.AccessRights -notlike 'Owner'
} | Select-Object -Property @{
name = 'Name'
expression = {
$SingleMailbox.Name
}
}, @{
name = 'UserPrincipalName'
expression = {
$SingleMailbox.UserPrincipalName
}
}, FolderName, @{
name = 'User'
expression = {
$_.User -join ','
}
}, @{
name = 'AccessRights'
expression = {
$_.AccessRights -join ','
}
}
# Cleanup
$MailboxFolderPermission = $null
}
# Update the counter
$MailboxCount++
Write-Verbose -Message ('Done with processing {0}' -f $SingleMailbox.UserPrincipalName)
}
#endregion MailboxLoop
#region Reporter
if ($MailboxPermissionReport)
{
$paramExportCsv = @{
Path = $Path
Force = $true
NoTypeInformation = $true
Confirm = $false
ErrorAction = 'Stop'
WarningAction = $CNT
}
$null = ($MailboxPermissionReport | Export-Csv @paramExportCsv)
}
else
{
Write-Warning -Message 'None of the Mailboxes has special permissions set'
}
#endregion Reporter
}
else
{
Write-Warning -Message 'No Mailboxes found that matches your search criteria'
}
}
end
{
#region Cleanup
$MailboxPermissionReport = $null
$AllMailboxes = $null
$MailboxCount = $null
$MailboxFolderPermission = $null
$ProgressStatus = $null
#endregion Cleanup
}
}
#region LICENSE
<#
BSD 3-Clause License
Copyright (c) 2021, enabling Technology
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#>
#endregion LICENSE
#region DISCLAIMER
<#
DISCLAIMER:
- Use at your own risk, etc.
- This is open-source software, if you find an issue try to fix it yourself. There is no support and/or warranty in any kind
- This is a third-party Software
- The developer of this Software is NOT sponsored by or affiliated with Microsoft Corp (MSFT) or any of its subsidiaries in any way
- The Software is not supported by Microsoft Corp (MSFT)
- By using the Software, you agree to the License, Terms, and any Conditions declared and described above
- If you disagree with any of the terms, and any conditions declared: Just delete it and build your own solution
#>
#endregion DISCLAIMER

View File

@@ -0,0 +1,335 @@
function Get-enMailboxPermissionReport
{
<#
.SYNOPSIS
Get a detailed mailbox permission report
.DESCRIPTION
Get a detailed mailbox permission report and exports this report to a given CSV file.
You can select only user-mailboxes, only shared-mailboxes or both for the reporting.
.PARAMETER Identity
The Identity parameter specifies the mailbox that you want to view.
You can use any value that uniquely identifies the mailbox.
Default is * (all)
.PARAMETER MailboxType
The type is the value for the regular RecipientTypeDetails.
The acceptable values for this parameter are:
- UserMailbox
- User
- SharedMailbox
- Shared
- All
The Default is ALL
.PARAMETER ResultSize
The ResultSize parameter specifies the maximum number of results to return.
If you want to return all requests that match the query, use unlimited for the value of this parameter.
The default value is unlimited.
.PARAMETER Path
Specifies the path to the CSV output file.
The default is 'C:\scripts\PowerShell\Reports\MailboxPermissionReport.csv'
.PARAMETER Encoding
Specifies the encoding for the exported CSV file.
The acceptable values for this parameter are:
- Unicode
- UTF7
- UTF8
- ASCII
- UTF32
- BigEndianUnicode
- Default
- OEM
Default is UTF8
.EXAMPLE
PS C:\> Get-enMailboxPermissionReport
Get a detailed mailbox permission report
.NOTES
Developed and tested with Exchange Online, it should work with on Premises Exchange 2010/2010/2016/2019
This is open-source software, if you find an issue try to fix it yourself.
There is no support and/or warranty in any kind
.LINK
http://www.enatec.io
.LINK
Get-Mailbox
.LINK
Get-RecipientPermission
.LINK
Export-Csv
#>
[CmdletBinding(ConfirmImpact = 'None')]
param
(
[Parameter(ValueFromPipeline = $true,
ValueFromPipelineByPropertyName = $true)]
[AllowEmptyString()]
[AllowEmptyCollection()]
[Alias('Mailbox', 'MailboxID', 'MailboxIdentity')]
[string]
$Identity = '*',
[Parameter(ValueFromPipeline = $true,
ValueFromPipelineByPropertyName = $true)]
[ValidateSet('UserMailbox', 'User', 'SharedMailbox', 'Shared', 'All', IgnoreCase = $true)]
[string]
$MailboxType = 'All',
[Parameter(ValueFromPipeline = $true,
ValueFromPipelineByPropertyName = $true)]
[AllowEmptyCollection()]
[AllowEmptyString()]
[Alias('MailboxResultSize')]
[string]
$ResultSize = 'Unlimited',
[Parameter(ValueFromPipeline = $true,
ValueFromPipelineByPropertyName = $true)]
[AllowEmptyCollection()]
[AllowEmptyString()]
[Alias('CsvReport', 'CsvFile')]
[string]
$Path = 'C:\scripts\PowerShell\Reports\MailboxPermissionReport.csv',
[Parameter(ValueFromPipeline = $true,
ValueFromPipelineByPropertyName = $true)]
[ValidateSet('Unicode', 'UTF7', 'UTF8', 'ASCII', 'UTF32', 'BigEndianUnicode', 'Default', 'OEM', IgnoreCase = $true)]
[AllowEmptyCollection()]
[AllowEmptyString()]
[Alias('CsvEncoding')]
[string]
$Encoding = 'UTF8'
)
begin
{
#region Cleanup
$MailboxPermissionReport = $null
$AllMailboxes = $null
#endregion Cleanup
#region Defaults
$SCT = 'SilentlyContinue'
$CNT = 'Continue'
if (-not ($Identity))
{
$Identity = '*'
}
if (-not ($MailboxType))
{
$MailboxType = 'All'
}
if (-not ($ResultSize))
{
$ResultSize = 'Unlimited'
}
if (-not ($Path))
{
$Path = 'C:\scripts\PowerShell\Reports\MailboxPermissionReport.csv'
}
if (-not ($Encoding))
{
$Encoding = 'UTF8'
}
#endregion Defaults
#region MailboxType
Write-Verbose -Message 'Get the mailboxes'
#region paramGetMailbox
$paramGetMailbox = @{
Identity = $Identity
ResultSize = $ResultSize
ErrorAction = $SCT
WarningAction = $CNT
}
#endregion paramGetMailbox
#region MailboxTypeSwitch
switch ($MailboxType)
{
UserMailbox
{
$paramWhereObject = @{
FilterScript = {
$_.RecipientTypeDetails -eq 'UserMailbox'
}
}
}
User
{
$paramWhereObject = @{
FilterScript = {
$_.RecipientTypeDetails -eq 'UserMailbox'
}
}
}
SharedMailbox
{
$paramWhereObject = @{
FilterScript = {
$_.RecipientTypeDetails -eq 'SharedMailbox'
}
}
}
Shared
{
$paramWhereObject = @{
FilterScript = {
$_.RecipientTypeDetails -eq 'SharedMailbox'
}
}
}
All
{
$paramWhereObject = @{
FilterScript = {
$_.RecipientTypeDetails -eq 'UserMailbox' -or $_.RecipientTypeDetails -eq 'SharedMailbox'
}
}
}
default
{
$paramWhereObject = @{
FilterScript = {
$_.RecipientTypeDetails -eq 'UserMailbox' -or $_.RecipientTypeDetails -eq 'SharedMailbox'
}
}
}
}
#endregion MailboxTypeSwitch
#region GetAllMailboxes
$AllMailboxes = (Get-Mailbox @paramGetMailbox | Where-Object @paramWhereObject | Sort-Object)
#endregion GetAllMailboxes
#endregion MailboxType
}
process
{
if ($AllMailboxes)
{
# Create a new object for the report
$MailboxPermissionReport = @()
# Create a counter for Write-Progress
$MailboxCounter = ($AllMailboxes | Measure-Object).Count
# Set the start counter for Write-Progress to 1
$MailboxCount = 1
#region MailboxLoop
Write-Verbose -Message 'Process all mailboxes'
ForEach ($SingleMailbox in $AllMailboxes)
{
# Update Write-Progress
$ProgressActivity = ('Working on Mailbox {0} of {1} ({2})' -f $MailboxCount, $MailboxCounter, $SingleMailbox.UserPrincipalName)
$ProgressStatus = ('Getting folders for mailbox: {0} ({1})' -f $SingleMailbox.DisplayName, $SingleMailbox.UserPrincipalName)
Write-Verbose -Message $ProgressStatus
$paramWriteProgress = @{
Status = $ProgressStatus
Activity = $ProgressActivity
PercentComplete = (($MailboxCount/$MailboxCounter) * 100)
}
Write-Progress @paramWriteProgress
$MailboxPermissionReport += $SingleMailbox | Get-MailboxPermission | Where-Object -FilterScript {
($_.IsInherited -eq $false) -and -not ($_.User -match 'NT AUTHORITY')
} | Select-Object -Property 'Identity', @{
Name = 'UserPrincipalName'
Expression = {
$SingleMailbox.UserPrincipalName
}
}, 'User', @{
Name = 'Access Rights'
Expression = {
$_.AccessRights -join ','
}
} -ErrorAction $CNT -WarningAction $CNT
}
#endregion MailboxLoop
#region Reporter
if ($MailboxPermissionReport)
{
$paramExportCsv = @{
Path = $Path
Force = $true
NoTypeInformation = $true
Confirm = $false
ErrorAction = 'Stop'
WarningAction = $CNT
}
$null = ($MailboxPermissionReport | Export-Csv @paramExportCsv)
}
else
{
Write-Warning -Message 'None of the Mailboxes has special permissions set'
}
#endregion Reporter
}
else
{
Write-Warning -Message 'No Mailboxes found that matches your search criteria'
}
}
end
{
#region Cleanup
$MailboxPermissionReport = $null
$AllMailboxes = $null
#endregion Cleanup
}
}
#region LICENSE
<#
BSD 3-Clause License
Copyright (c) 2021, enabling Technology
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#>
#endregion LICENSE
#region DISCLAIMER
<#
DISCLAIMER:
- Use at your own risk, etc.
- This is open-source software, if you find an issue try to fix it yourself. There is no support and/or warranty in any kind
- This is a third-party Software
- The developer of this Software is NOT sponsored by or affiliated with Microsoft Corp (MSFT) or any of its subsidiaries in any way
- The Software is not supported by Microsoft Corp (MSFT)
- By using the Software, you agree to the License, Terms, and any Conditions declared and described above
- If you disagree with any of the terms, and any conditions declared: Just delete it and build your own solution
#>
#endregion DISCLAIMER

View File

@@ -0,0 +1,335 @@
function Get-enMailboxSendAsReport
{
<#
.SYNOPSIS
Get a detailed mailbox Send permission report
.DESCRIPTION
Get a detailed mailbox Send permission report and exports this report to a given CSV file.
You can select only user-mailboxes, only shared-mailboxes or both for the reporting.
.PARAMETER Identity
The Identity parameter specifies the mailbox that you want to view.
You can use any value that uniquely identifies the mailbox.
Default is * (all)
.PARAMETER MailboxType
The type is the value for the regular RecipientTypeDetails.
The acceptable values for this parameter are:
- UserMailbox
- User
- SharedMailbox
- Shared
- All
The Default is ALL
.PARAMETER ResultSize
The ResultSize parameter specifies the maximum number of results to return.
If you want to return all requests that match the query, use unlimited for the value of this parameter.
The default value is unlimited.
.PARAMETER Path
Specifies the path to the CSV output file.
The default is 'C:\scripts\PowerShell\Reports\MailboxSendAsReport.csv'
.PARAMETER Encoding
Specifies the encoding for the exported CSV file.
The acceptable values for this parameter are:
- Unicode
- UTF7
- UTF8
- ASCII
- UTF32
- BigEndianUnicode
- Default
- OEM
Default is UTF8
.EXAMPLE
PS C:\> Get-enMailboxSendAsReport
Get a detailed mailbox permission report
.NOTES
Developed and tested with Exchange Online, it should work with on Premises Exchange 2010/2010/2016/2019
This is open-source software, if you find an issue try to fix it yourself.
There is no support and/or warranty in any kind
.LINK
http://www.enatec.io
.LINK
Get-Mailbox
.LINK
Get-RecipientPermission
.LINK
Export-Csv
#>
[CmdletBinding(ConfirmImpact = 'None')]
param
(
[Parameter(ValueFromPipeline = $true,
ValueFromPipelineByPropertyName = $true)]
[AllowEmptyString()]
[AllowEmptyCollection()]
[Alias('Mailbox', 'MailboxID', 'MailboxIdentity')]
[string]
$Identity = '*',
[Parameter(ValueFromPipeline = $true,
ValueFromPipelineByPropertyName = $true)]
[ValidateSet('UserMailbox', 'User', 'SharedMailbox', 'Shared', 'All', IgnoreCase = $true)]
[string]
$MailboxType = 'All',
[Parameter(ValueFromPipeline = $true,
ValueFromPipelineByPropertyName = $true)]
[AllowEmptyCollection()]
[AllowEmptyString()]
[Alias('MailboxResultSize')]
[string]
$ResultSize = 'Unlimited',
[Parameter(ValueFromPipeline = $true,
ValueFromPipelineByPropertyName = $true)]
[AllowEmptyCollection()]
[AllowEmptyString()]
[Alias('CsvReport', 'CsvFile')]
[string]
$Path = 'C:\scripts\PowerShell\Reports\MailboxSendAsReport.csv',
[Parameter(ValueFromPipeline = $true,
ValueFromPipelineByPropertyName = $true)]
[ValidateSet('Unicode', 'UTF7', 'UTF8', 'ASCII', 'UTF32', 'BigEndianUnicode', 'Default', 'OEM', IgnoreCase = $true)]
[AllowEmptyCollection()]
[AllowEmptyString()]
[Alias('CsvEncoding')]
[string]
$Encoding = 'UTF8'
)
begin
{
#region Cleanup
$MailboxPermissionReport = $null
$AllMailboxes = $null
#endregion Cleanup
#region Defaults
$SCT = 'SilentlyContinue'
$CNT = 'Continue'
if (-not ($Identity))
{
$Identity = '*'
}
if (-not ($MailboxType))
{
$MailboxType = 'All'
}
if (-not ($ResultSize))
{
$ResultSize = 'Unlimited'
}
if (-not ($Path))
{
$Path = 'C:\scripts\PowerShell\Reports\MailboxSendAsReport.csv'
}
if (-not ($Encoding))
{
$Encoding = 'UTF8'
}
#endregion Defaults
#region MailboxType
Write-Verbose -Message 'Get the mailboxes'
#region paramGetMailbox
$paramGetMailbox = @{
Identity = $Identity
ResultSize = $ResultSize
ErrorAction = $SCT
WarningAction = $CNT
}
#endregion paramGetMailbox
#region MailboxTypeSwitch
switch ($MailboxType)
{
UserMailbox
{
$paramWhereObject = @{
FilterScript = {
$_.RecipientTypeDetails -eq 'UserMailbox'
}
}
}
User
{
$paramWhereObject = @{
FilterScript = {
$_.RecipientTypeDetails -eq 'UserMailbox'
}
}
}
SharedMailbox
{
$paramWhereObject = @{
FilterScript = {
$_.RecipientTypeDetails -eq 'SharedMailbox'
}
}
}
Shared
{
$paramWhereObject = @{
FilterScript = {
$_.RecipientTypeDetails -eq 'SharedMailbox'
}
}
}
All
{
$paramWhereObject = @{
FilterScript = {
$_.RecipientTypeDetails -eq 'UserMailbox' -or $_.RecipientTypeDetails -eq 'SharedMailbox'
}
}
}
default
{
$paramWhereObject = @{
FilterScript = {
$_.RecipientTypeDetails -eq 'UserMailbox' -or $_.RecipientTypeDetails -eq 'SharedMailbox'
}
}
}
}
#endregion MailboxTypeSwitch
#region GetAllMailboxes
$AllMailboxes = (Get-Mailbox @paramGetMailbox | Where-Object @paramWhereObject | Sort-Object)
#endregion GetAllMailboxes
#endregion MailboxType
}
process
{
if ($AllMailboxes)
{
# Create a new object for the report
$MailboxPermissionReport = @()
# Create a counter for Write-Progress
$MailboxCounter = ($AllMailboxes | Measure-Object).Count
# Set the start counter for Write-Progress to 1
$MailboxCount = 1
#region MailboxLoop
Write-Verbose -Message 'Process all mailboxes'
ForEach ($SingleMailbox in $AllMailboxes)
{
# Update Write-Progress
$ProgressActivity = ('Working on Mailbox {0} of {1} ({2})' -f $MailboxCount, $MailboxCounter, $SingleMailbox.UserPrincipalName)
$ProgressStatus = ('Getting folders for mailbox: {0} ({1})' -f $SingleMailbox.DisplayName, $SingleMailbox.UserPrincipalName)
Write-Verbose -Message $ProgressStatus
$paramWriteProgress = @{
Status = $ProgressStatus
Activity = $ProgressActivity
PercentComplete = (($MailboxCount/$MailboxCounter) * 100)
}
Write-Progress @paramWriteProgress
$MailboxPermissionReport += $SingleMailbox | Get-RecipientPermission | Where-Object -FilterScript {
($_.IsInherited -eq $false) -and -not ($_.Trustee -match 'NT AUTHORITY')
} | Select-Object -Property 'Identity', @{
Name = 'UserPrincipalName'
Expression = {
$SingleMailbox.UserPrincipalName
}
}, 'Trustee', @{
Name = 'Access Rights'
Expression = {
$_.AccessRights -join ','
}
} -ErrorAction $CNT -WarningAction $CNT
}
#endregion MailboxLoop
#region Reporter
if ($MailboxPermissionReport)
{
$paramExportCsv = @{
Path = $Path
Force = $true
NoTypeInformation = $true
Confirm = $false
ErrorAction = 'Stop'
WarningAction = $CNT
}
$null = ($MailboxPermissionReport | Export-Csv @paramExportCsv)
}
else
{
Write-Warning -Message 'None of the Mailboxes has special permissions set'
}
#endregion Reporter
}
else
{
Write-Warning -Message 'No Mailboxes found that matches your search criteria'
}
}
end
{
#region Cleanup
$MailboxPermissionReport = $null
$AllMailboxes = $null
#endregion Cleanup
}
}
#region LICENSE
<#
BSD 3-Clause License
Copyright (c) 2021, enabling Technology
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#>
#endregion LICENSE
#region DISCLAIMER
<#
DISCLAIMER:
- Use at your own risk, etc.
- This is open-source software, if you find an issue try to fix it yourself. There is no support and/or warranty in any kind
- This is a third-party Software
- The developer of this Software is NOT sponsored by or affiliated with Microsoft Corp (MSFT) or any of its subsidiaries in any way
- The Software is not supported by Microsoft Corp (MSFT)
- By using the Software, you agree to the License, Terms, and any Conditions declared and described above
- If you disagree with any of the terms, and any conditions declared: Just delete it and build your own solution
#>
#endregion DISCLAIMER

View File

@@ -0,0 +1,29 @@
BSD 3-Clause License
Copyright (c) 2021, enabling Technology <http://enatec.io>
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@@ -0,0 +1,370 @@
function Search-MailboxItemDeletion
{
<#
.SYNOPSIS
Search for deletions in mailboxes
.DESCRIPTION
Search for deletions in mailboxes, single or all
.PARAMETER Days
Day (period) to search, max. 90 (or 30, based on your O365/M365 license).
The default is 7 (for the last 7 days)
Minimum is 1, maximum is 90. This will be checked
.PARAMETER Mailbox
Mailbox Address
e.g. info@contoso.com
.PARAMETER All
Get all deletes, for all mailboxes
.EXAMPLE
PS C:\> Search-MailboxItemDeletion -All
Get all deletes, for all mailboxes
.EXAMPLE
PS C:\> Search-MailboxItemDeletion -Days 2 | Where-Object -FilterScript { $_.Folder -ne 'Deleted Items' }
Get all deletes of the last 2 days, for all mailboxes, but we exclude one Folder.
.EXAMPLE
PS C:\> Search-MailboxItemDeletion -Days 7 | Where-Object -FilterScript { $_.Folder -ne 'Deleted Items' }
Get all deletes of the last 7 days, for all mailboxes, but we exclude one Folder.
.EXAMPLE
PS C:\> Search-MailboxItemDeletion -Days 30 | Where-Object -FilterScript { ($_.Folder -ne 'Drafts') -and ($_.Action -ne 'SoftDelete') }
Get all deletes of the last 30 days, for all mailboxes, but we exclude one Folder and the 'SoftDelete' action
.EXAMPLE
PS C:\> Search-MailboxItemDeletion -Days 21 -All
Get all deletes for the last 21 days, for all mailboxes
.EXAMPLE
PS C:\> Search-MailboxItemDeletion -All | Out-GridView
Search for Deletions in all mailboxes and open the result in the GridView (e.g. for filtering)
.EXAMPLE
PS C:\> Search-MailboxItemDeletion -Mailbox 'info@contoso.com'
Search for Deletions in the mailbox 'info@contoso.com'
.EXAMPLE
PS C:\> Search-MailboxItemDeletion -Mailbox 'info@contoso.com' | Select-Object -Property 'Timestamp', 'Action', 'Status' , 'User', 'Mailbox', 'Subject', 'Folder', 'Client', 'ClientIP'
Search for Deletions in the mailbox 'info@contoso.com', and get a few more properties (e.g. Status, Client, and ClientIP).
Might be handy to see from where it was triggered and what client was used.
.EXAMPLE
PS C:\> Search-MailboxItemDeletion -Mailbox 'info@contoso.com' | Export-CSV -NoTypeInformation -Path c:\scripts\PowerShell\exports\ExchangeOnlineMailboxDeletes.csv
Search for Deletions in the mailbox 'info@contoso.com' and export the result into a CSV File (e.g. for a basic reporting or further investigation in Excel)
.OUTPUTS
array
.LINK
Search-UnifiedAuditLog
.NOTES
For now, the following properties are supported:
Action string
AppId string
Client string
ClientIP string
External bool
ExternalAccess bool
Folder string
InternalLogonType int
InternetMessageId string
LogonType int
Mailbox string
MailboxGuid string
MessageId string
OrganizationId string
OrganizationName string
OriginatingServer string
SessionId string
Status string
Subject string
TimeStamp string
User string
By default, the following properties are returned (all others can be selected):
TimeStamp string
Action string
User string
Mailbox string
Subject string
Folder string
Requirements:
PowerShell or Windows PowerShell
Exchange Online connection (e.g. the installed Module and you need to be connected with a user that has rights to use Search-UnifiedAuditLog)
A future version might support Wildcards in the Mailbox parameter and/or multi Mailbox searches.
Workaround: use Where-Object with a powerful FilterScript!
#>
[CmdletBinding(DefaultParameterSetName = 'All',
ConfirmImpact = 'None')]
[OutputType([array])]
param
(
[Parameter(ValueFromPipeline,
ValueFromPipelineByPropertyName)]
[ValidateNotNullOrEmpty()]
[ValidateNotNull()]
[int]
$Days = 7,
[Parameter(ParameterSetName = 'Single', HelpMessage = 'Mailbox Address e.g. info@contoso.com',
Mandatory,
ValueFromPipeline,
ValueFromPipelineByPropertyName)]
[ValidateNotNullOrEmpty()]
[ValidateNotNull()]
[Alias('MailboxName', 'MailboxAddress')]
[string]
$Mailbox,
[Parameter(ParameterSetName = 'All')]
[switch]
$All
)
begin
{
# Garbage Collection
[GC]::Collect()
# Cleanup
$Records = $null
# TimeSpan
$StartDate = (Get-Date).AddDays(-$Days)
# Now
$EndDate = (Get-Date)
#region HelperFunctions
function Get-StandardMembersFromPSObject
{
<#
.SYNOPSIS
Filter the given properties from a given Object
.DESCRIPTION
Filter the given properties from a given Object
.PARAMETER InputObject
The input object, must be a psobject.
.PARAMETER Properties
The properties to select from the given input object.
Multiple values needs to separated by a comma.
.EXAMPLE
Get-StandardMembersFromPSObject -InputObject Value -Properties Value
Describe what this call does
.OUTPUTS
psobject
.NOTES
Just an internal Helper function
.LINK
https://learn-powershell.net/2013/08/03/quick-hits-set-the-default-property-display-in-powershell-on-custom-objects/
.LINK
http://stackoverflow.com/questions/1369542/can-you-set-an-objects-defaultdisplaypropertyset-in-a-powershell-v2-script/1891215#1891215
.INPUTS
psobject, string
#>
[CmdletBinding(ConfirmImpact = 'None')]
[OutputType([psobject])]
param
(
[Parameter(Mandatory,
ValueFromPipeline,
ValueFromPipelineByPropertyName,
HelpMessage = 'The input object, must be a psobject.')]
[ValidateNotNull()]
[ValidateNotNullOrEmpty()]
[psobject]
$InputObject,
[Parameter(ValueFromPipeline,
ValueFromPipelineByPropertyName)]
[ValidateNotNull()]
[ValidateNotNullOrEmpty()]
[Alias('DefaultProperties')]
[string[]]
$Properties = $null
)
process
{
try
{
$defaultDisplayPropertySet = (New-Object -TypeName System.Management.Automation.PSPropertySet -ArgumentList ('DefaultDisplayPropertySet', [string[]]$Properties))
$PSStandardMembers = ([Management.Automation.PSMemberInfo[]]@($defaultDisplayPropertySet))
$InputObject | Add-Member -MemberType MemberSet -Name PSStandardMembers -Value $PSStandardMembers -Force
}
catch
{
#region ErrorHandler
# get error record
[Management.Automation.ErrorRecord]$e = $_
# retrieve information about runtime error
$info = [PSCustomObject]@{
Exception = $e.Exception.Message
Reason = $e.CategoryInfo.Reason
Target = $e.CategoryInfo.TargetName
Script = $e.InvocationInfo.ScriptName
Line = $e.InvocationInfo.ScriptLineNumber
Column = $e.InvocationInfo.OffsetInLine
}
# output information. Post-process collected info, and log info (optional)
$info | Out-String | Write-Verbose
#endregion ErrorHandler
}
}
}
#endregion HelperFunctions
}
process
{
# Get the UnifiedAuditLog Data, with the delete operations
$Records = (Search-UnifiedAuditLog -StartDate $StartDate -EndDate $EndDate -Operations 'HardDelete', 'SoftDelete')
# Do we have a result
if ($Records)
{
Write-Verbose -Message ('Processing ' + $Records.Count + ' audit records...')
# Create a new Object
$Report = [Collections.Generic.List[Object]]::new()
foreach ($Rec in $Records)
{
$AuditData = (ConvertFrom-Json -InputObject $Rec.Auditdata)
if ($AuditData.ResultStatus -eq 'PartiallySucceeded')
{
$MessageSubject = '# Not fully deleted by' + $AuditData.ClientInfoString + ' #'
}
else
{
$MessageSubject = ($AuditData.AffectedItems.Subject -split '\n')[0]
}
$ReportLine = [PSCustomObject] @{
TimeStamp = (Get-Date -Date ($AuditData.CreationTime) -Format g)
User = $AuditData.UserId
Action = $AuditData.Operation
Status = $AuditData.ResultStatus
Mailbox = $AuditData.MailboxOwnerUPN
MailboxGuid = $AuditData.MailboxGuid
Subject = $MessageSubject
MessageId = ($AuditData.AffectedItems.Id -split '\n')[0]
InternetMessageId = ($AuditData.AffectedItems.InternetMessageId -split '\n')[0]
Folder = $AuditData.Folder.Path.Split('\')[1]
Client = $AuditData.ClientInfoString
AppId = $AuditData.AppId
ClientIP = $AuditData.ClientIP
External = $AuditData.ExternalAccess
SessionId = $AuditData.SessionId
ExternalAccess = $AuditData.ExternalAccess
InternalLogonType = $AuditData.InternalLogonType
LogonType = $AuditData.LogonType
OrganizationName = $AuditData.OrganizationName
OrganizationId = $AuditData.OrganizationId
OriginatingServer = $AuditData.OriginatingServer
}
# Define the default properties and support Select-Object
Get-StandardMembersFromPSObject -InputObject $ReportLine -Properties 'Timestamp', 'Action', 'User', 'Mailbox', 'Subject', 'Folder'
# Add to the reporting
$Report.Add($ReportLine)
}
$Records = $null
}
else
{
Write-Output -InputObject 'No deletion records found.'
break
}
# Create a new array object
$Output = @()
# Single or all ?
switch ($PsCmdlet.ParameterSetName)
{
'Single'
{
$Output = ($Report | Where-Object -FilterScript {
# You might want to tweak the filter to support Wildcards or more the one mailbox
$_.Mailbox -eq $Mailbox
})
}
'All'
{
$Output = ($Report | Sort-Object -Property Mailbox)
}
}
# Cleanup
$Report = $null
}
end
{
# Just dump the result to the terminal
$Output
# Cleanup
$Output = $null
# Garbage Collection
[GC]::Collect()
}
}
#region LICENSE
<#
BSD 3-Clause License
Copyright (c) 2021, enabling Technology
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#>
#endregion LICENSE
#region DISCLAIMER
<#
DISCLAIMER:
- Use at your own risk, etc.
- This is open-source software, if you find an issue try to fix it yourself. There is no support and/or warranty in any kind
- This is a third-party Software
- The developer of this Software is NOT sponsored by or affiliated with Microsoft Corp (MSFT) or any of its subsidiaries in any way
- The Software is not supported by Microsoft Corp (MSFT)
- By using the Software, you agree to the License, Terms, and any Conditions declared and described above
- If you disagree with any of the terms, and any conditions declared: Just delete it and build your own solution
#>
#endregion DISCLAIMER

View File

@@ -0,0 +1,167 @@
<#
.SYNOPSIS
Cleanup Microsoft Teams Client
.DESCRIPTION
Cleanup Microsoft Teams Client by deleting several local cache files
.EXAMPLE
PS C:\> .\Clear-MicrosoftTeamsClientCache.ps1
Cleanup Microsoft Teams Client by deleting several local cache files
.EXAMPLE
PS C:\> .\Clear-MicrosoftTeamsClientCache.ps1 -Verbose
Cleanup Microsoft Teams Client by deleting several local cache files, but be verbose while doing it
.EXAMPLE
PS C:\> .\Clear-MicrosoftTeamsClientCache.ps1 -WhatIf
Cleanup Microsoft Teams Client by deleting several local cache files - Dry Run!!!
.NOTES
Due to some issues, Windows is not supported at this time!
#>
[CmdletBinding(ConfirmImpact = 'Medium',
SupportsShouldProcess)]
param ()
if ($IsMacOS -eq $true)
{
$AppDataBasePath = '~/Library/Application Support/Microsoft/Teams/'
}
else
{
Write-Warning -Message 'Due to some issues, Windows is not supported at this time!'
exit 1
$AppDataBasePath = ($env:APPDATA + '\Microsoft\teams\')
}
#region BoundParameters
if (($PSCmdlet.MyInvocation.BoundParameters['Verbose']).IsPresent)
{
$VerboseValue = $true
}
else
{
$VerboseValue = $false
}
if (($PSCmdlet.MyInvocation.BoundParameters['Debug']).IsPresent)
{
$DebugValue = $true
}
else
{
$DebugValue = $false
}
if (($PSCmdlet.MyInvocation.BoundParameters['WhatIf']).IsPresent)
{
$WhatIfValue = $true
}
else
{
$WhatIfValue = $false
}
#endregion BoundParameters
#region
$paramGetChildItem = @{
Verbose = $VerboseValue
Debug = $DebugValue
Recurse = $true
ErrorAction = 'SilentlyContinue'
}
$paramRemoveItem = @{
Verbose = $VerboseValue
Debug = $DebugValue
WhatIf = $WhatIfValue
Confirm = $false
Force = $true
Recurse = $true
ErrorAction = 'SilentlyContinue'
}
#endregion
#region
if ($PSCmdlet.ShouldProcess('Microsoft Teams Client', 'Hard Kill'))
{
$null = (Get-Process -Name Teams -ErrorAction SilentlyContinue | Stop-Process -Force -ErrorAction SilentlyContinue)
Start-Sleep -Seconds 2
}
#endregion
Get-ChildItem -Path ($AppDataBasePath + 'blob_storage') @paramGetChildItem -Verbose -Debug | ForEach-Object -Process {
Remove-Item -Path $_.FullName @paramRemoveItem -WhatIf
}
Get-ChildItem -Path ($AppDataBasePath + 'databases') @paramGetChildItem | ForEach-Object -Process {
Remove-Item -Path $_.FullName @paramRemoveItem
}
Get-ChildItem -Path ($AppDataBasePath + 'Cache') @paramGetChildItem | ForEach-Object -Process {
Remove-Item -Path $_.FullName @paramRemoveItem
}
Get-ChildItem -Path ($AppDataBasePath + 'gpucache') @paramGetChildItem | ForEach-Object -Process {
Remove-Item -Path $_.FullName @paramRemoveItem
}
Get-ChildItem -Path ($AppDataBasePath + 'IndexedDB') @paramGetChildItem | ForEach-Object -Process {
Remove-Item -Path $_.FullName -Confirm:$false -Force -Recurse -ErrorAction SilentlyContinue
}
Get-ChildItem -Path ($AppDataBasePath + 'Local Storage') @paramGetChildItem | ForEach-Object -Process {
Remove-Item -Path $_.FullName @paramRemoveItem
}
Get-ChildItem -Path ($AppDataBasePath + 'tmp') @paramGetChildItem | ForEach-Object -Process {
Remove-Item -Path $_.FullName @paramRemoveItem
}
Get-ChildItem -Path $AppDataBasePath -Include 'old_logs_*.txt', 'logs.txt', 'in_progress_download_metadata_store' @paramGetChildItem | ForEach-Object -Process {
Remove-Item -Path $_.FullName @paramRemoveItem
}
if (Test-Path -Path ($AppDataBasePath + 'installTime.txt'))
{
$InstallDateInput = (Get-Content -Path ($AppDataBasePath + 'installTime.txt'))
$Culture = (New-Object -TypeName System.Globalization.CultureInfo -ArgumentList ('de-DE'))
$InstallDate = (Get-Date -Date $InstallDateInput -Format ($Culture.DateTimeFormat.ShortDatePattern))
Write-Output -InputObject ('Latest Version of Microsoft Teams from: {0}' -f $InstallDate)
}
#region LICENSE
<#
BSD 3-Clause License
Copyright (c) 2021, enabling Technology
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#>
#endregion LICENSE
#region DISCLAIMER
<#
DISCLAIMER:
- Use at your own risk, etc.
- This is open-source software, if you find an issue try to fix it yourself. There is no support and/or warranty in any kind
- This is a third-party Software
- The developer of this Software is NOT sponsored by or affiliated with Microsoft Corp (MSFT) or any of its subsidiaries in any way
- The Software is not supported by Microsoft Corp (MSFT)
- By using the Software, you agree to the License, Terms, and any Conditions declared and described above
- If you disagree with any of the terms, and any conditions declared: Just delete it and build your own solution
#>
#endregion DISCLAIMER

View File

@@ -0,0 +1,535 @@
<#
.SYNOPSIS
Microsoft Teams Client customization settings via PowerShell
.DESCRIPTION
Microsoft Teams Client customization settings via PowerShell
.EXAMPLE
PS C:\> .\Default_MicrosoftTeams_DesktopConfig.ps1
.EXAMPLE
PS C:\> .\Default_MicrosoftTeams_DesktopConfig.ps1 -verbose
.NOTES
Refactored and extended version of Desktop-Config-Json.ps1 by eshlomo1
.LINK
https://github.com/eshlomo1/MS_Teams/blob/master/Desktop-Config-Json.ps1
.LINK
https://www.eshlomo.us/microsoft-teams-client-personalization-with-powershell/
#>
[CmdletBinding(ConfirmImpact = 'None')]
param ()
begin
{
#region DefaultSettings
$AppPrefSetOpenAsHidden = $false
$AppPrefSetOpenAtLogin = $false
$AppPrefSetRegisterAsIMProvider = $true
$AppPrefSetRunningOnClose = $false
$NotificationWindowOnClose = $true
$OverrideOpenAsHiddenProperty = $true
$IsAppFirstRun = $false
$CurrentWebLanguage = 'en-us'
#endregion DefaultSettings
#region Cleanup
$ChangedConfig = $null
$SourceConfigFile = $null
$Teams = $null
#endregion Cleanup
#region SetConfigPath
if (($PSVersionTable.PSEdition -eq 'Core') + ($PSVersionTable.Platform -eq 'Unix') -and ($PSVersionTable.OS -like 'Darwin*'))
{
# OK, macOS is supported
$SourceConfigFile = ($Env:HOME + '/Library/Application Support/Microsoft/Teams/desktop-config.json')
}
elseif (($PSVersionTable.PSEdition -eq 'Core') + ($PSVersionTable.Platform -eq 'Unix') -and ($PSVersionTable.OS -like 'Linux*'))
{
# Sorry, Linux is not supported...
$paramWriteError = @{
Message = 'Sorry, Linux is not supported...'
Exception = 'Sorry, Linux is not supported!'
Category = 'NotImplemented'
ErrorAction = 'Stop'
}
Write-Error @paramWriteError
exit 1
}
else
{
# Windows? Really??? OK, sure this is supported
$SourceConfigFile = ($env:userprofile + '\AppData\Roaming\Microsoft\Teams\desktop-config.json')
}
#endregion SetConfigPath
}
process
{
if (Test-Path -Path $SourceConfigFile -ErrorAction SilentlyContinue -WarningAction Continue)
{
#region GetConfig
try
{
# Splat the parameters
$paramGetContent = @{
Path = $SourceConfigFile
Force = $true
ErrorAction = 'Stop'
WarningAction = 'Continue'
}
$Teams = (Get-Content @paramGetContent | ConvertFrom-Json -ErrorAction Stop)
# Cleanup
$paramGetContent = $null
}
catch
{
# get error record
[Management.Automation.ErrorRecord]$e = $_
# retrieve information about runtime error
$info = [PSCustomObject]@{
Exception = $e.Exception.Message
Reason = $e.CategoryInfo.Reason
Target = $e.CategoryInfo.TargetName
Script = $e.InvocationInfo.ScriptName
Line = $e.InvocationInfo.ScriptLineNumber
Column = $e.InvocationInfo.OffsetInLine
}
# output information. Post-process collected info, and log info (optional)
$info | Out-String | Write-Verbose
Write-Error -Message $e.Exception.Message -ErrorAction Stop -Exception $e.Exception -TargetObject $e.CategoryInfo.TargetName
break
}
#endregion GetConfig
#region
if ($Teams.appPreferenceSettings)
{
if ($Teams.appPreferenceSettings.openAsHidden)
{
if ($Teams.appPreferenceSettings.openAsHidden -ne $AppPrefSetOpenAsHidden)
{
Write-Verbose -Message 'Value of openAsHidden will be changed to the desired default'
$Teams.appPreferenceSettings.openAsHidden = $AppPrefSetOpenAsHidden
# Set the change indicator
$ChangedConfig = $true
Write-Verbose -Message 'Value of openAsHidden was changed to the desired default'
}
else
{
Write-Verbose -Message 'Value of openAsHidden is unchanged'
}
}
else
{
Write-Verbose -Message 'The Parameter openAsHidden will be created with the desired default value'
# Splat the parameters
$paramAddMember = @{
MemberType = 'NoteProperty'
Name = 'openAsHidden'
Value = $AppPrefSetOpenAsHidden
}
$null = ($Teams.appPreferenceSettings | Add-Member @paramAddMember)
# Cleanup
$paramAddMember = $null
# Set the change indicator
$ChangedConfig = $true
Write-Verbose -Message 'The Parameter openAsHidden was created with the desired default value'
}
if ($Teams.appPreferenceSettings.openAtLogin)
{
if ($Teams.appPreferenceSettings.openAtLogin -ne $AppPrefSetOpenAtLogin)
{
Write-Verbose -Message 'Value of openAtLogin will be changed to the desired default'
$Teams.appPreferenceSettings.openAtLogin = $AppPrefSetOpenAtLogin
# Set the change indicator
$ChangedConfig = $true
Write-Verbose -Message 'Value of openAtLogin was changed to the desired default'
}
else
{
Write-Verbose -Message 'Value of openAtLogin is unchanged'
}
}
else
{
Write-Verbose -Message 'The Parameter openAtLogin will be created with the desired default value'
# Splat the parameters
$paramAddMember = @{
MemberType = 'NoteProperty'
Name = 'openAtLogin'
Value = $AppPrefSetOpenAtLogin
}
$null = ($Teams.appPreferenceSettings | Add-Member @paramAddMember)
# Cleanup
$paramAddMember = $null
# Set the change indicator
$ChangedConfig = $true
Write-Verbose -Message 'The Parameter openAtLogin was created with the desired default value'
}
if (($PSVersionTable.PSEdition -eq 'Core') + ($PSVersionTable.Platform -eq 'Unix') -and ($PSVersionTable.OS -like 'Darwin*') )
{
Write-Verbose -Message 'The setting registerAsIMProvider is not supported on macOS...'
}
else
{
if ($Teams.appPreferenceSettings.registerAsIMProvider)
{
if ($Teams.appPreferenceSettings.registerAsIMProvider -ne $AppPrefSetRegisterAsIMProvider)
{
Write-Verbose -Message 'Value of registerAsIMProvider will be changed to the desired default'
$Teams.appPreferenceSettings.registerAsIMProvider = $AppPrefSetRegisterAsIMProvider
# Set the change indicator
$ChangedConfig = $true
Write-Verbose -Message 'Value of registerAsIMProvider was changed to the desired default'
}
else
{
Write-Verbose -Message 'Value of registerAsIMProvider is unchanged'
}
}
else
{
Write-Verbose -Message 'The Parameter registerAsIMProvider will be created with the desired default value'
# Splat the parameters
$paramAddMember = @{
MemberType = 'NoteProperty'
Name = 'registerAsIMProvider'
Value = $AppPrefSetRegisterAsIMProvider
}
$null = ($Teams.appPreferenceSettings | Add-Member @paramAddMember)
# Cleanup
$paramAddMember = $null
# Set the change indicator
$ChangedConfig = $true
Write-Verbose -Message 'The Parameter registerAsIMProvider was created with the desired default value'
}
}
if ($Teams.appPreferenceSettings.runningOnClose)
{
if ($Teams.appPreferenceSettings.runningOnClose -ne $AppPrefSetRunningOnClose)
{
Write-Verbose -Message 'Value of runningOnClose will be changed to the desired default'
$Teams.appPreferenceSettings.runningOnClose = $AppPrefSetRunningOnClose
# Set the change indicator
$ChangedConfig = $true
Write-Verbose -Message 'Value of runningOnClose was changed to the desired default'
}
else
{
Write-Verbose -Message 'Value of runningOnClose is unchanged'
}
}
else
{
Write-Verbose -Message 'The Parameter runningOnClose will be created with the desired default value'
# Splat the parameters
$paramAddMember = @{
MemberType = 'NoteProperty'
Name = 'runningOnClose'
Value = $AppPrefSetRunningOnClose
}
$null = ($Teams.appPreferenceSettings | Add-Member @paramAddMember)
# Cleanup
$paramAddMember = $null
# Set the change indicator
$ChangedConfig = $true
Write-Verbose -Message 'The Parameter runningOnClose was created with the desired default value'
}
}
if ($Teams.notificationWindowOnClose)
{
if ($Teams.notificationWindowOnClose -ne $NotificationWindowOnClose)
{
Write-Verbose -Message 'Value of notificationWindowOnClose will be changed to the desired default'
$Teams.notificationWindowOnClose = $NotificationWindowOnClose
# Set the change indicator
$ChangedConfig = $true
Write-Verbose -Message 'Value of notificationWindowOnClose was changed to the desired default'
}
else
{
Write-Verbose -Message 'Value of notificationWindowOnClose is unchanged'
}
}
else
{
Write-Verbose -Message 'The Parameter currentWebLanguage will be created with the desired default value'
# Splat the parameters
$paramAddMember = @{
MemberType = 'NoteProperty'
Name = 'notificationWindowOnClose'
Value = $NotificationWindowOnClose
}
$null = ($Teams | Add-Member @paramAddMember)
# Cleanup
$paramAddMember = $null
# Set the change indicator
$ChangedConfig = $true
Write-Verbose -Message 'The Parameter currentWebLanguage was created with the desired default value'
}
if ($Teams.overrideOpenAsHiddenProperty)
{
if ($Teams.overrideOpenAsHiddenProperty -ne $OverrideOpenAsHiddenProperty)
{
Write-Verbose -Message 'Value of overrideOpenAsHiddenProperty will be changed to the desired default'
$Teams.overrideOpenAsHiddenProperty = $OverrideOpenAsHiddenProperty
# Set the change indicator
$ChangedConfig = $true
Write-Verbose -Message 'Value of overrideOpenAsHiddenProperty was changed to the desired default'
}
else
{
Write-Verbose -Message 'Value of overrideOpenAsHiddenProperty is unchanged'
}
}
else
{
Write-Verbose -Message 'The Parameter overrideOpenAsHiddenProperty will be created with the desired default value'
# Splat the parameters
$paramAddMember = @{
MemberType = 'NoteProperty'
Name = 'overrideOpenAsHiddenProperty'
Value = $OverrideOpenAsHiddenProperty
}
$null = ($Teams | Add-Member @paramAddMember)
# Cleanup
$paramAddMember = $null
# Set the change indicator
$ChangedConfig = $true
Write-Verbose -Message 'The Parameter overrideOpenAsHiddenProperty was created with the desired default value'
}
if ($Teams.isAppFirstRun)
{
if ($Teams.isAppFirstRun -ne $IsAppFirstRun)
{
Write-Verbose -Message 'Value of isAppFirstRun will be changed to the desired default'
$Teams.isAppFirstRun = $IsAppFirstRun
# Set the change indicator
$ChangedConfig = $true
Write-Verbose -Message 'Value of isAppFirstRun was changed to the desired default'
}
else
{
Write-Verbose -Message 'Value of isAppFirstRun is unchanged'
}
}
else
{
Write-Verbose -Message 'The Parameter isAppFirstRun will be created with the desired default value'
# Splat the parameters
$paramAddMember = @{
MemberType = 'NoteProperty'
Name = 'isAppFirstRun'
Value = $false
}
$null = ($Teams | Add-Member @paramAddMember)
# Cleanup
$paramAddMember = $null
# Set the change indicator
$ChangedConfig = $true
Write-Verbose -Message 'The Parameter isAppFirstRun was created with the desired default value'
}
if ($Teams.currentWebLanguage)
{
if ($Teams.currentWebLanguage -ne $CurrentWebLanguage)
{
Write-Verbose -Message 'Value of currentWebLanguage will be changed to the desired default'
$Teams.currentWebLanguage = $CurrentWebLanguage
# Set the change indicator
$ChangedConfig = $true
Write-Verbose -Message 'Value of currentWebLanguage was changed to the desired default'
}
else
{
Write-Verbose -Message 'Value of currentWebLanguage is unchanged'
}
}
else
{
Write-Verbose -Message 'The Parameter currentWebLanguage will be created with the desired default value'
# Splat the parameters
$paramAddMember = @{
MemberType = 'NoteProperty'
Name = 'currentWebLanguage'
Value = $CurrentWebLanguage
}
$null = ($Teams | Add-Member @paramAddMember)
#Cleanup
$paramAddMember = $null
# Set the change indicator
$ChangedConfig = $true
Write-Verbose -Message 'The Parameter currentWebLanguage was created with the desired default value'
}
#endregion
#region SaveNewConfig
if ($ChangedConfig)
{
Write-Verbose -Message 'Changed configuration will be saved'
try
{
# Splat the parameters
$paramConvertToJson = @{
Compress = $true
ErrorAction = 'Stop'
WarningAction = 'Continue'
}
$paramSetContent = @{
Path = $SourceConfigFile
Force = $true
Confirm = $false
ErrorAction = 'Stop'
WarningAction = 'Continue'
}
$null = ($Teams | ConvertTo-Json @paramConvertToJson | Set-Content @paramSetContent)
# Cleanup
$paramConvertToJson = $null
$paramSetContent = $null
$Teams = $null
}
catch
{
# get error record
[Management.Automation.ErrorRecord]$e = $_
# retrieve information about runtime error
$info = [PSCustomObject]@{
Exception = $e.Exception.Message
Reason = $e.CategoryInfo.Reason
Target = $e.CategoryInfo.TargetName
Script = $e.InvocationInfo.ScriptName
Line = $e.InvocationInfo.ScriptLineNumber
Column = $e.InvocationInfo.OffsetInLine
}
# output information. Post-process collected info, and log info (optional)
$info | Out-String | Write-Verbose
Write-Error -Message $e.Exception.Message -ErrorAction Stop -Exception $e.Exception -TargetObject $e.CategoryInfo.TargetName
break
}
Write-Verbose -Message 'Changed configuration was saved'
}
else
{
Write-Verbose -Message 'No changes made to the configuration'
}
#endregion SaveNewConfig
}
else
{
Write-Warning -Message 'No Configuration File for Microsoft Teams was found.'
exit 1
}
}
#region LICENSE
<#
BSD 3-Clause License
Copyright (c) 2021, enabling Technology
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#>
#endregion LICENSE
#region DISCLAIMER
<#
DISCLAIMER:
- Use at your own risk, etc.
- This is open-source software, if you find an issue try to fix it yourself. There is no support and/or warranty in any kind
- This is a third-party Software
- The developer of this Software is NOT sponsored by or affiliated with Microsoft Corp (MSFT) or any of its subsidiaries in any way
- The Software is not supported by Microsoft Corp (MSFT)
- By using the Software, you agree to the License, Terms, and any Conditions declared and described above
- If you disagree with any of the terms, and any conditions declared: Just delete it and build your own solution
#>
#endregion DISCLAIMER

View File

@@ -0,0 +1,65 @@
#requires -Version 2.0 -Modules MicrosoftTeams
<#
.SYNOPSIS
Get a List of external Microsoft Teams Applications
.DESCRIPTION
Get a List of external Microsoft Teams Applications for the tenant.
.EXAMPLE
PS C:\> .\Get-AllExternalTeamsApps.ps1
Get a List of external Microsoft Teams Applications for the tenant.
.EXAMPLE
PS C:\> .\Get-AllExternalTeamsApps.ps1 | Select-Object -Property DisplayName, DistributionMethod
Get a List of external Microsoft Teams Applications for the tenant.
.NOTES
You need to use the Microsoft Teams Cmdlets module
If you don't have, install it from the gallery:
Install-Module -Name MicrosoftTeams
#>
[CmdletBinding(ConfirmImpact = 'None')]
param ()
<#
Simple filter: External Apps will have the ExternalId field filled,
where store apps (from the Microsoft Teams App Store) not.
#>
Get-TeamsApp | Where-Object -FilterScript {
$_.ExternalId
}
#region LICENSE
<#
BSD 3-Clause License
Copyright (c) 2021, enabling Technology
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#>
#endregion LICENSE
#region DISCLAIMER
<#
DISCLAIMER:
- Use at your own risk, etc.
- This is open-source software, if you find an issue try to fix it yourself. There is no support and/or warranty in any kind
- This is a third-party Software
- The developer of this Software is NOT sponsored by or affiliated with Microsoft Corp (MSFT) or any of its subsidiaries in any way
- The Software is not supported by Microsoft Corp (MSFT)
- By using the Software, you agree to the License, Terms, and any Conditions declared and described above
- If you disagree with any of the terms, and any conditions declared: Just delete it and build your own solution
#>
#endregion DISCLAIMER

View File

@@ -0,0 +1,717 @@
#requires -Version 3.0 -Modules MicrosoftTeams
<#
.SYNOPSIS
Collects assigned phone numbers from Microsoft Teams
.DESCRIPTION
This script queries Microsoft Teams for assigned numbers and displays in a formatted table with the option to export the report in several formats
During processing LineURI's are run against a regex pattern to extract the DDI/DID and the extension to a separate column
This script collects Microsoft Teams objects including:
Users, Meeting Rooms, Online Application Instances (Resource Accounts)
.PARAMETER OutputType
Define the Script Output
Valid values are:
CONSOLE - Dump a formatted list into the console
HTML - Create a simple HTML report with Tables. Only here to be compatible to our older version
XML - Create a simple Extensible Markup Language (XML) report
YAML - Create a simple YAML Ain't Markup Language (YAML) report
JSON - Create a simple JavaScript Object Notation (JSON) report. Handy if you need to upload the data via WebServices/APIs
CSV - Create a simple comma-separated values (CSV) report. This is perfect for re-use within Excel, or other applications
If you leave it empty (this is the default), the object will be dumped to the console!
This can become handy, if you use this script to generate the report and re-use it in the pipe or your own application
.PARAMETER Path
Where to store the Report File
Default is 'C:\scripts\PowerShell\logs\'
.PARAMETER DateFormat
Use the format for Get-Date
Default is 'yyyyMMdd-HHmmUTC'
.PARAMETER UTC
Use ToUniversalTime for the Date Strings
Default is $true
.PARAMETER Report
Define what to report.
Valid values are:
Users - Report numbers assigned to users
MeetingRooms - Report numbers assigned to Meetings Room accounts
ResourceAccounts - Report numbers assigned to Applications. (Auto Attendants (AA) and/or Call Queues (CQ) are supported)
All - All assigned numbers (all above merged into one report)
Default is 'All'
.EXAMPLE
PS C:\> .\Get-TeamsAssignedNumbers.ps1
The Report will be dumped to the console (unformatted)
.EXAMPLE
PS C:\> .\Get-TeamsAssignedNumbers.ps1 -OutputType CONSOLE -Report MeetingRooms
Dump a formatted report for numbers assigned to Meeting Rooms into the console
.EXAMPLE
PS C:\> .\Get-TeamsAssignedNumbers.ps1 -OutputType CONSOLE -Report ResourceAccounts
Dump a formatted report for numbers assigned to Resource Accounts into the console
.EXAMPLE
PS C:\> .\Get-TeamsAssignedNumbers.ps1 -OutputType CONSOLE -Report Users
Dump a formatted report for numbers assigned to Resource Accounts into the console
This will contain function users for Resource Accounts and Meeting Rooms, but they will be shown as user object!
.EXAMPLE
PS C:\> .\Get-TeamsAssignedNumbers.ps1 -OutputType CONSOLE -Report all
Dump a formatted report for every assigned number into the console
.EXAMPLE
PS C:\> .\Get-TeamsAssignedNumbers.ps1 -OutputType HTML
Create a simple HTML report with Tables.
.EXAMPLE
PS C:\> .\Get-TeamsAssignedNumbers.ps1 -OutputType XML
Create a simple Extensible Markup Language (XML) report
.EXAMPLE
PS C:\> .\Get-TeamsAssignedNumbers.ps1 -OutputType YAML
Create a simple YAML Ain't Markup Language (YAML) report
.EXAMPLE
PS C:\> .\Get-TeamsAssignedNumbers.ps1 -OutputType JSON
Create a simple JavaScript Object Notation (JSON) report.
.EXAMPLE
PS C:\> .\Get-TeamsAssignedNumbers.ps1 -OutputType CSV
Create a simple comma-separated values (CSV) report.
.LINK
https://github.com/ucgeek/Get-TeamsAssignedNumbers
.LINK
https://github.com/ucgeek/Get-TeamsAssignedNumbers/blob/master/LICENSE
.NOTES
Based on the work off Andrew Morpeth (@ucgeek and https://ucgeek.co/)
Licensed under the GNU General Public License v3.0 terms (by @ucgeek)
REQUIREMENTS:
If you haven't already, you will need to install the MicrosoftTeams PowerShell module
The script assumes, you are connect to the Teams/Skype for Business Online Service of Microsoft Office 365
#>
[CmdletBinding(ConfirmImpact = 'None')]
param
(
[Parameter(ValueFromPipeline,
ValueFromPipelineByPropertyName)]
[AllowNull()]
[AllowEmptyString()]
[AllowEmptyCollection()]
[ValidateSet('CONSOLE', 'HTML', 'XML', 'YAML', 'JSON', 'CSV', IgnoreCase = $true)]
[Alias('ReportFormat')]
[string]
$OutputType = $null,
[Parameter(ValueFromPipeline,
ValueFromPipelineByPropertyName)]
[AllowNull()]
[AllowEmptyString()]
[AllowEmptyCollection()]
[string]
$Path = 'C:\scripts\PowerShell\logs',
[Parameter(ValueFromPipeline,
ValueFromPipelineByPropertyName)]
[AllowNull()]
[AllowEmptyString()]
[AllowEmptyCollection()]
[string]
$DateFormat = 'yyyyMMdd-HHmmUTC',
[Parameter(ValueFromPipeline,
ValueFromPipelineByPropertyName)]
[Alias('UseUTC')]
[switch]
$UTC,
[Parameter(ValueFromPipeline,
ValueFromPipelineByPropertyName)]
[AllowEmptyCollection()]
[AllowEmptyString()]
[AllowNull()]
[ValidateSet('Users', 'MeetingRooms', 'ResourceAccounts', 'All', IgnoreCase = $true)]
[Alias('ReportType')]
[string[]]
$Report = 'All'
)
begin
{
#region BoundParameters
if (($PSCmdlet.MyInvocation.BoundParameters['Verbose']).IsPresent)
{
$VerboseValue = $true
}
else
{
$VerboseValue = $false
}
if (($PSCmdlet.MyInvocation.BoundParameters['Debug']).IsPresent)
{
$DebugValue = $true
}
else
{
$DebugValue = $false
}
#endregion BoundParameters
#region DateToUTC
if (-not ($UTC))
{
$UTC = $true
}
#endregion DateToUTC
#region UseDateUTC
if (($UTC -eq $true) -and ($DateFormat))
{
$FileName = ('MicrosoftTeamsAssignedNumbers_' + ((Get-Date).ToUniversalTime()).ToString($DateFormat))
}
#endregion UseDateUTC
#region UseDateFormat
if ($DateFormat)
{
$FileName = ('MicrosoftTeamsAssignedNumbers_' + ((Get-Date).ToString($DateFormat)))
}
else
{
# This is the default
$FileName = ('MicrosoftTeamsAssignedNumbers_' + (Get-Date -Format s).replace(':', '-'))
}
#endregion UseDateFormat
#region PathMangle
if ($Path)
{
# Save to a given PATH
$FilePath = ($Path + '\' + $FileName)
}
else
{
# Save here (where the script was started)
$FilePath = ('.\' + $FileName)
}
#endregion PathMangle
#region Regex
# Regex values
$LineURIRegex = '^(?:tel:)?(?:\+)?(\d+)(?:;ext=(\d+))?(?:;([\w-]+))?$'
#endregion Regex
#region ReportType
if (-not ($Report))
{
$Report = 'All'
}
#endregion ReportType
# Cleanup
$ReportData = @()
}
process
{
#region Users
if (($Report -eq 'Users') -or ($Report -eq 'All'))
{
# Get Users with LineURI
$UsersLineURI = $null
$paramGetCsOnlineUser = @{
ResultSize = 30000
Verbose = $VerboseValue
Debug = $DebugValue
Filter = {
(LineURI -ne $null)
}
ErrorAction = 'SilentlyContinue'
WarningAction = 'SilentlyContinue'
}
$paramSelectObject = @{
Property = 'UserPrincipalName', 'LineURI', 'DisplayName', 'FirstName', 'LastName', 'Enabled', 'SipAddress'
Verbose = $VerboseValue
Debug = $DebugValue
}
$UsersLineURI = (Get-CsOnlineUser @paramGetCsOnlineUser | Select-Object @paramSelectObject)
if ($UsersLineURI)
{
Write-Verbose -Message 'Processing User Numbers'
foreach ($ReportingItem in $UsersLineURI)
{
$Matches = @()
($ReportingItem.PhoneNumber -match $LineURIRegex | Out-Null)
$ReportingObject = (New-Object -TypeName System.Object)
$null = ($ReportingObject | Add-Member -MemberType NoteProperty -Name 'UserPrincipalName' -Value $ReportingItem.UserPrincipalName)
$null = ($ReportingObject | Add-Member -MemberType NoteProperty -Name 'LineURI' -Value $ReportingItem.LineURI)
$null = ($ReportingObject | Add-Member -MemberType NoteProperty -Name 'DDI' -Value $Matches[1])
$null = ($ReportingObject | Add-Member -MemberType NoteProperty -Name 'Ext' -Value $Matches[2])
$null = ($ReportingObject | Add-Member -MemberType NoteProperty -Name 'DisplayName' -Value $ReportingItem.DisplayName)
$null = ($ReportingObject | Add-Member -MemberType NoteProperty -Name 'FirstName' -Value $ReportingItem.FirstName)
$null = ($ReportingObject | Add-Member -MemberType NoteProperty -Name 'LastName' -Value $ReportingItem.LastName)
$null = ($ReportingObject | Add-Member -MemberType NoteProperty -Name 'SipAddress' -Value $($ReportingItem.SipAddress -replace 'sip:', ''))
$null = ($ReportingObject | Add-Member -MemberType NoteProperty -Name 'Enabled' -Value $ReportingItem.Enabled)
$null = ($ReportingObject | Add-Member -MemberType NoteProperty -Name 'Type' -Value 'User')
# Add to array
$null = ($ReportData += $ReportingObject)
}
}
}
#endregion Users
#region MeetingRooms
if (($Report -eq 'MeetingRooms') -or ($Report -eq 'All'))
{
# Get meeting room numbers
$MeetingRoomLineURI = $null
$paramGetCsMeetingRoom = @{
ResultSize = 10000
Verbose = $VerboseValue
Debug = $DebugValue
Filter = {
LineURI -ne $null
}
ErrorAction = 'SilentlyContinue'
WarningAction = 'SilentlyContinue'
}
$paramSelectObject = @{
Property = 'UserPrincipalName', 'LineURI', 'DisplayName', 'Enabled', 'SipAddress'
Verbose = $VerboseValue
Debug = $DebugValue
}
$MeetingRoomLineURI = (Get-CsMeetingRoom @paramGetCsMeetingRoom | Select-Object @paramSelectObject)
if ($MeetingRoomLineURI)
{
Write-Verbose -Message 'Processing Meeting Room Numbers'
foreach ($ReportingItem in $MeetingRoomLineURI)
{
$Matches = @()
($ReportingItem.PhoneNumber -match $LineURIRegex | Out-Null)
$ReportingObject = (New-Object -TypeName System.Object)
$null = ($ReportingObject | Add-Member -MemberType NoteProperty -Name 'UserPrincipalName' -Value $ReportingItem.UserPrincipalName)
$null = ($ReportingObject | Add-Member -MemberType NoteProperty -Name 'LineURI' -Value $ReportingItem.LineURI)
$null = ($ReportingObject | Add-Member -MemberType NoteProperty -Name 'DDI' -Value $Matches[1])
$null = ($ReportingObject | Add-Member -MemberType NoteProperty -Name 'Ext' -Value $Matches[2])
$null = ($ReportingObject | Add-Member -MemberType NoteProperty -Name 'DisplayName' -Value $ReportingItem.DisplayName)
$null = ($ReportingObject | Add-Member -MemberType NoteProperty -Name 'SipAddress' -Value $($ReportingItem.SipAddress -replace 'sip:', ''))
$null = ($ReportingObject | Add-Member -MemberType NoteProperty -Name 'Enabled' -Value $ReportingItem.Enabled)
$null = ($ReportingObject | Add-Member -MemberType NoteProperty -Name 'Type' -Value 'Meeting Room')
# Remove existing User entry (Rooms have an user object as well)
if ($ReportData.UserPrincipalName -contains $ReportingItem.UserPrincipalName)
{
$ReportData = ($ReportData | Where-Object -FilterScript {
($_.UserPrincipalName -ne $ReportingItem.UserPrincipalName)
})
}
# Add to array
$null = ($ReportData += $ReportingObject)
}
}
}
#endregion MeetingRooms
#region ResourceAccounts
if (($Report -eq 'ResourceAccounts') -or ($Report -eq 'All'))
{
# Get online resource accounts
$OnlineApplicationInstanceLineURI = $null
$paramGetCsOnlineApplicationInstance = @{
Force = $true
ResultSize = 10000
Verbose = $VerboseValue
Debug = $DebugValue
ErrorAction = 'SilentlyContinue'
WarningAction = 'SilentlyContinue'
}
$paramSelectObject = @{
Property = 'UserPrincipalName', 'DisplayName', 'PhoneNumber', 'ApplicationId', 'Enabled'
Verbose = $VerboseValue
Debug = $DebugValue
}
$OnlineApplicationInstanceLineURI = (Get-CsOnlineApplicationInstance @paramGetCsOnlineApplicationInstance | Where-Object -FilterScript {
($_.PhoneNumber -ne $null)
} -ErrorAction SilentlyContinue -WarningAction SilentlyContinue | Select-Object @paramSelectObject)
if ($OnlineApplicationInstanceLineURI)
{
Write-Verbose -Message 'Processing Online Application Instances (Resource Accounts) Numbers'
foreach ($ReportingItem in $OnlineApplicationInstanceLineURI)
{
$Matches = @()
($ReportingItem.PhoneNumber -match $LineURIRegex | Out-Null)
<#
Workaround:
Get-CsOnlineApplicationInstance does not return an "Enabled" and "SipAddress" field,
so we try to re-use any existing object information
Will not work all the time, only if regular users are reported as well!
#>
if ($ReportData.UserPrincipalName -contains $ReportingItem.UserPrincipalName)
{
# Cleanup
$WorkaroundInfo = $null
$WorkaroundInfo = ($ReportData | Where-Object -FilterScript {
($_.UserPrincipalName -eq $ReportingItem.UserPrincipalName)
} | Select-Object -Property 'Enabled', 'SipAddress')
if (($WorkaroundInfo).Enabled)
{
$isAppEnabled = (($WorkaroundInfo).Enabled)
}
else
{
$isAppEnabled = 'unknown'
}
if (($WorkaroundInfo).SipAddress)
{
$isSipAddress = (($WorkaroundInfo).SipAddress)
}
else
{
$isSipAddress = 'unknown'
}
}
$ReportingObject = (New-Object -TypeName System.Object)
$null = ($ReportingObject | Add-Member -MemberType NoteProperty -Name 'UserPrincipalName' -Value $ReportingItem.UserPrincipalName)
$null = ($ReportingObject | Add-Member -MemberType NoteProperty -Name 'LineURI' -Value $ReportingItem.PhoneNumber)
$null = ($ReportingObject | Add-Member -MemberType NoteProperty -Name 'DDI' -Value $Matches[1])
$null = ($ReportingObject | Add-Member -MemberType NoteProperty -Name 'Ext' -Value $Matches[2])
$null = ($ReportingObject | Add-Member -MemberType NoteProperty -Name 'DisplayName' -Value $ReportingItem.DisplayName)
$null = ($ReportingObject | Add-Member -MemberType NoteProperty -Name 'SipAddress' -Value $isSipAddress)
$null = ($ReportingObject | Add-Member -MemberType NoteProperty -Name 'Enabled' -Value $isAppEnabled)
$null = ($ReportingObject | Add-Member -MemberType NoteProperty -Name 'Type' -Value $(
if ($ReportingItem.ApplicationId -eq 'ce933385-9390-45d1-9512-c8d228074e07')
{
'Auto Attendant Resource Account'
}
elseif ($ReportingItem.ApplicationId -eq '11cd3e2e-fccb-42ad-ad00-878b93575e07')
{
'Call Queue Resource Account'
}
else
{
'Unknown Resource Account'
}
))
# Remove existing User entry (Apps have an user object as well)
if ($ReportData.UserPrincipalName -contains $ReportingItem.UserPrincipalName)
{
$ReportData = ($ReportData | Where-Object -FilterScript {
($_.UserPrincipalName -ne $ReportingItem.UserPrincipalName)
})
}
# Add to array
$null = ($ReportData += $ReportingObject)
}
}
}
#endregion ResourceAccounts
# Sort the Array data, based on the LineURI object
$paramSortObject = @{
Property = 'LineURI'
Verbose = $VerboseValue
Debug = $DebugValue
}
$ReportData = ($ReportData | Sort-Object @paramSortObject)
#region Output
switch ($OutputType)
{
CSV
{
$FilePath = ($FilePath + '.csv')
$paramConvertToCsv = @{
Delimiter = ','
NoTypeInformation = $true
Verbose = $VerboseValue
Debug = $DebugValue
ErrorAction = 'Continue'
WarningAction = 'Continue'
}
$PsCsv = ($ReportData | ConvertTo-Csv @paramConvertToCsv)
$paramOutFile = @{
FilePath = $FilePath
Force = $true
Append = $false
Encoding = 'utf8'
ErrorAction = 'Continue'
WarningAction = 'Continue'
Verbose = $VerboseValue
Debug = $DebugValue
}
($PsCsv | Out-File @paramOutFile)
Write-Verbose -Message ('Your CSV report was saved to: {0}' -f $FilePath)
}
JSON
{
$FilePath = ($FilePath + '.json')
$paramConvertToJson = @{
Depth = 5
ErrorAction = 'Continue'
WarningAction = 'Continue'
Verbose = $VerboseValue
Debug = $DebugValue
}
$PsJson = @($ReportData | ConvertTo-Json @paramConvertToJson)
$paramOutFile = @{
FilePath = $FilePath
Force = $true
Append = $false
Encoding = 'utf8'
ErrorAction = 'Continue'
WarningAction = 'Continue'
Verbose = $VerboseValue
Debug = $DebugValue
}
($PsJson | Out-File @paramOutFile)
Write-Verbose -Message ('Your JSON report was saved to: {0}' -f $FilePath)
}
YAML
{
if (Get-Command -Name 'ConvertTo-Yaml' -ErrorAction SilentlyContinue)
{
$FilePath = ($FilePath + '.yml')
<#
Workaround for ConvertTo-Yaml
#>
$paramJsonWorkaround = @{
ErrorAction = 'Continue'
WarningAction = 'Continue'
Verbose = $VerboseValue
Debug = $DebugValue
}
$ReportData = @($ReportData | ConvertTo-Json @paramJsonWorkaround | ConvertFrom-Json @paramJsonWorkaround)
$paramConvertToYaml = @{
Data = $ReportData
Force = $true
ErrorAction = 'Continue'
WarningAction = 'Continue'
Verbose = $VerboseValue
Debug = $DebugValue
}
$PsYaml = (ConvertTo-Yaml @paramConvertToYaml)
$paramOutFile = @{
FilePath = $FilePath
Force = $true
Append = $false
Encoding = 'utf8'
ErrorAction = 'Continue'
WarningAction = 'Continue'
Verbose = $VerboseValue
Debug = $DebugValue
}
($PsYaml | Out-File @paramOutFile)
Write-Verbose -Message ('Your YAML report was saved to: {0}' -f $FilePath)
}
else
{
$paramWriteError = @{
Exception = 'The ConvertTo-Yaml command was not found'
Message = 'Please ensure, that the ''powershell-yaml'' module is installed.'
Category = 'NotInstalled'
RecommendedAction = 'Please use ''Install-Module -Name powershell-yaml'' to install the required module!'
ErrorAction = 'Stop'
}
Write-Error @paramWriteError
}
}
XML
{
$FilePath = ($FilePath + '.xml')
$paramConvertToXml = @{
As = 'Stream'
InputObject = $ReportData
NoTypeInformation = $true
ErrorAction = 'Continue'
WarningAction = 'Continue'
Verbose = $VerboseValue
Debug = $DebugValue
}
$PsXML = (ConvertTo-Xml @paramConvertToXml)
$paramOutFile = @{
FilePath = $FilePath
Force = $true
Append = $false
Encoding = 'utf8'
ErrorAction = 'Continue'
WarningAction = 'Continue'
Verbose = $VerboseValue
Debug = $DebugValue
}
($PsXML | Out-File @paramOutFile)
Write-Verbose -Message ('Your XML report was saved to: {0}' -f $FilePath)
}
HTML
{
$FilePath = ($FilePath + '.html')
$Header = @"
<title>Microsoft Teams assigned phone number report</title>
<meta charset='UTF-8'>
<meta name='viewport' content='width=device-width, initial-scale=1'>
<style>
table {
border-width: 1px;
border-style: solid;
border-color: black;
border-collapse: collapse;
}
th {
border-width: 1px;
padding: 3px;
border-style: solid;
border-color: black;
background-color: #6495ED;
}
td {
border-width: 1px;
padding: 3px;
border-style: solid;
border-color: black;
}
</style>
"@
$htmlParams = @{
Title = 'Microsoft Teams assigned phone number report'
Head = $Header
body = '<h3>Microsoft Teams assigned phone number report</h3>'
PreContent = '<p>The following Phone numbers are assigned in Microsoft Teams:</p>'
PostContent = '<p><i>Last updated: ' + ((Get-Date).ToUniversalTime()).ToString('HH:MM dd.MM.yyyy (UTC)') + '</i></p>'
Verbose = $VerboseValue
Debug = $DebugValue
}
$PsHtml = ($ReportData | ConvertTo-Html @htmlParams)
$paramOutFile = @{
FilePath = $FilePath
Force = $true
Append = $false
Encoding = 'utf8'
ErrorAction = 'Continue'
WarningAction = 'Continue'
Verbose = $VerboseValue
Debug = $DebugValue
}
($PsHtml | Out-File @paramOutFile)
Write-Verbose -Message ('Your HTML report was saved to: {0}' -f $FilePath)
}
CONSOLE
{
$paramFormatTable = @{
AutoSize = $true
Property = 'UserPrincipalName', 'LineURI', 'DDI', 'Ext', 'DisplayName', 'Type'
Verbose = $VerboseValue
Debug = $DebugValue
}
($ReportData | Format-Table @paramFormatTable)
Write-Verbose -Message 'Formated Object was dumped'
}
default
{
$ReportData
Write-Verbose -Message 'Unformated Object was dumped'
}
}
#endregion Output
}
end
{
# Cleanup
$ReportData = $null
}
#region LICENSE
<#
BSD 3-Clause License
Copyright (c) 2021, enabling Technology
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#>
#endregion LICENSE
#region DISCLAIMER
<#
DISCLAIMER:
- Use at your own risk, etc.
- This is open-source software, if you find an issue try to fix it yourself. There is no support and/or warranty in any kind
- This is a third-party Software
- The developer of this Software is NOT sponsored by or affiliated with Microsoft Corp (MSFT) or any of its subsidiaries in any way
- The Software is not supported by Microsoft Corp (MSFT)
- By using the Software, you agree to the License, Terms, and any Conditions declared and described above
- If you disagree with any of the terms, and any conditions declared: Just delete it and build your own solution
#>
#endregion DISCLAIMER

View File

@@ -0,0 +1,276 @@
function Get-TeamsServiceNumbers
{
<#
.SYNOPSIS
Get the Phone numbers assigned to Teams/SfB Services
.DESCRIPTION
Get the Phone numbers assigned to Teams/SfB Services
Supported are AutoAttendant and/or CallQueue
.PARAMETER AutoAttendant
Get the numbers assigned to AutoAttendant(s)
.PARAMETER CallQueue
Get the numbers assigned to CallQueue(s)
.PARAMETER All
Get all Numbers, assignee to AutoAttendant(s) and CallQueue(s)
.PARAMETER LeaveTel
Normally the function dumps phone numbers with a stripped tel:
With this switch the function will dump it with the leading tel:
.PARAMETER Export
Export the result to a CSV
.PARAMETER Path
Path for the CSV Export
.EXAMPLE
PS C:\> Get-TeamsServiceNumbers -All
Get all Services numbers, AutoAttendant(s) and CallQueue(s), and dump them to the terminal
.EXAMPLE
PS C:\> Get-TeamsServiceNumbers -All -Export -Path '.\TeamsServiceNumbers.csv'
Get all Services numbers, AutoAttendant(s) and CallQueue(s), and export them to the given CSV file '.\TeamsServiceNumbers.csv'
TeamsServiceNumbers.csv is in the directory where the user is right now (and calls the function)
.EXAMPLE
PS C:\> Get-TeamsServiceNumbers -All -Export -Path 'c:\temp\TeamsServiceNumbers.csv'
Get all Services numbers, AutoAttendant(s) and CallQueue(s), and export them to the given CSV file 'c:\temp\TeamsServiceNumbers.csv'
.EXAMPLE
PS C:\> Get-TeamsServiceNumbers -All -Export
Get all Services numbers, AutoAttendant(s) and CallQueue(s), and export them to a CSV
The funtion will ask for the Path to the CSV File
.EXAMPLE
PS C:\> Get-TeamsServiceNumbers -AutoAttendant
Get all AutoAttendant(s) Services numbers and dump them to the terminal
.EXAMPLE
PS C:\> Get-TeamsServiceNumbers -AA
Get all AutoAttendant(s) Services numbers and dump them to the terminal
Same as above, but use the Alias (Shorter)
.EXAMPLE
PS C:\> Get-TeamsServiceNumbers -CallQueue
Get all CallQueue(s) Services numbers and dump them to the terminal
.EXAMPLE
PS C:\> Get-TeamsServiceNumbers -CQ
Get all CallQueue(s) Services numbers and dump them to the terminal
Same as above, but use the Alias (Shorter)
.NOTES
Additional information about the function.
#>
[CmdletBinding(DefaultParameterSetName = 'AllNumbers',
ConfirmImpact = 'None')]
[OutputType([psobject])]
param
(
[Parameter(ParameterSetName = 'AANumbers',
ValueFromPipeline = $true,
ValueFromPipelineByPropertyName = $true)]
[Alias('AA')]
[switch]
$AutoAttendant = $null,
[Parameter(ParameterSetName = 'CQNumbers',
ValueFromPipeline = $true,
ValueFromPipelineByPropertyName = $true)]
[Alias('CQ')]
[switch]
$CallQueue = $null,
[Parameter(ParameterSetName = 'AllNumbers',
ValueFromPipeline = $true,
ValueFromPipelineByPropertyName = $true)]
[Alias('Any')]
[switch]
$All,
[Parameter(ParameterSetName = '__AllParameterSets',
ValueFromPipeline = $true,
ValueFromPipelineByPropertyName = $true)]
[switch]
$LeaveTel = $null,
[Parameter(ParameterSetName = '__AllParameterSets',
ValueFromPipeline = $true,
ValueFromPipelineByPropertyName = $true)]
[Alias('ExportCsv', 'CSV')]
[switch]
$Export = $false
)
dynamicparam
{
if ($PSBoundParameters['Export'])
{
# The PATH parameter is only needed if -Export is given
$PathAttribute = New-Object System.Management.Automation.ParameterAttribute
$PathAttribute.Mandatory = $true
$PathAttribute.HelpMessage = "Path for the CSV Export:"
$PathAttribute.ValueFromPipeline = $true
$PathAttribute.ValueFromPipelineByPropertyName = $true
$PathAttribute.ParameterSetName = '__AllParameterSets'
$attributeCollection = New-Object System.Collections.ObjectModel.Collection[System.Attribute]
$attributeCollection.Add($PathAttribute)
$PathParam = New-Object System.Management.Automation.RuntimeDefinedParameter('Path', [String], $attributeCollection)
$paramDictionary = New-Object System.Management.Automation.RuntimeDefinedParameterDictionary
$paramDictionary.Add('Path', $PathParam)
$paramDictionary
}
}
begin
{
switch ($PsCmdlet.ParameterSetName)
{
'AANumbers'
{
$AutoAttendant = $true
}
'CQNumbers'
{
$CallQueue = $true
}
'AllNumbers'
{
$All = $true
}
default
{
$All = $true
}
}
if ($PSBoundParameters.Path)
{
$Path = $PSBoundParameters.Path
}
$NumberReport = @()
}
process
{
if (($AutoAttendant) -or ($All))
{
foreach ($AA in (Get-CsAutoAttendant -ErrorAction Continue))
{
foreach ($AppInstance in $AA.ApplicationInstances)
{
$AAName = $AA.Name
$AppPhoneNum = $null
if ($LeaveTel)
{
$AppPhoneNum = ((Get-CsOnlineApplicationInstance -Identity $AppInstance -ErrorAction Continue).PhoneNumber)
}
else
{
$AppPhoneNum = (((Get-CsOnlineApplicationInstance -Identity $AppInstance -ErrorAction Continue).PhoneNumber).replace('tel:', ''))
}
Write-Verbose ('AutoAttendant ' + $AA.Name + ' has ' + $AppPhoneNum + ' assigned')
$NewRow = $null
$NewRow = [PSCustomObject][ordered]@{
Name = ($AA.Name)
Number = ($AppPhoneNum)
Type = 'AutoAttendant'
}
$NumberReport += $newrow
}
}
}
if (($CallQueue) -or ($All))
{
foreach ($CQ in (Get-CsCallQueue -ErrorAction Continue))
{
foreach ($AppInstance in $CQ.ApplicationInstances)
{
$CQName = $null
$CQName = $CQ.Name
$AppPhoneNum = $null
if ($LeaveTel)
{
$AppPhoneNum = ((Get-CsOnlineApplicationInstance -Identity $AppInstance -ErrorAction Continue).PhoneNumber)
}
else
{
$AppPhoneNum = (((Get-CsOnlineApplicationInstance -Identity $AppInstance -ErrorAction Continue).PhoneNumber).replace('tel:', ''))
}
Write-Verbose ('CallQueue ' + $CQ.Name + ' has ' + $AppPhoneNum + ' assigned')
$NewRow = $null
$NewRow = [PSCustomObject][ordered]@{
Name = ($CQ.Name)
Number = ($AppPhoneNum)
Type = 'CallQueue'
}
$NumberReport += $newrow
}
}
}
}
end
{
if (($PSBoundParameters['Export']) -or ($Path))
{
$NumberReport | Export-Csv -Path $Path -NoTypeInformation -Encoding UTF8 -ErrorAction Stop -WarningAction Continue
Write-Verbose -Message $NumberReport
}
else
{
$NumberReport
}
}
}
#region LICENSE
<#
BSD 3-Clause License
Copyright (c) 2021, enabling Technology
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#>
#endregion LICENSE
#region DISCLAIMER
<#
DISCLAIMER:
- Use at your own risk, etc.
- This is open-source software, if you find an issue try to fix it yourself. There is no support and/or warranty in any kind
- This is a third-party Software
- The developer of this Software is NOT sponsored by or affiliated with Microsoft Corp (MSFT) or any of its subsidiaries in any way
- The Software is not supported by Microsoft Corp (MSFT)
- By using the Software, you agree to the License, Terms, and any Conditions declared and described above
- If you disagree with any of the terms, and any conditions declared: Just delete it and build your own solution
#>
#endregion DISCLAIMER

View File

@@ -0,0 +1,446 @@
function Get-bdcMicrosoftTeamsReporting
{
<#
.SYNOPSIS
Get a Report for all Microsoft Teams Teams
.DESCRIPTION
Get a Report for all Microsoft Teams Teams
.PARAMETER Connect
Executes Connect-MicrosoftTeams for you
.PARAMETER Disconnect
Executes Disconnect-MicrosoftTeams for you as soon as the report is generated
.PARAMETER GiphyDetails
Include Giphy Details in the report
.PARAMETER MemesDetails
Include Memes Details in the report
.PARAMETER GuestDetails
Include Guest Details in the report
.PARAMETER Detailed
Report some more Details about the Teams.
.PARAMETER AllDetails
All of the Details are reported, might be a bit verbose for some.
.EXAMPLE
PS C:\> Get-bdcMicrosoftTeamsReporting
Get a Report for all Microsoft Teams Teams
.EXAMPLE
PS C:\> Get-bdcMicrosoftTeamsReporting | Where-Object -FilterScript {
$_.owners -eq 0
} | Select-Object -ExpandProperty DisplayName
Find all Teams without an owner.
.EXAMPLE
PS C:\> Get-bdcMicrosoftTeamsReporting | Where-Object -FilterScript {
$_.owners -eq 1
} | Select-Object -ExpandProperty DisplayName | ForEach-Object -Process {
Write-Warning -Message "Looks like $_ is an orphaned objects, it has no owner!" -ErrorAction Continue
}
Find all Teams without an owner. Teams without an owner are bad teams...
.EXAMPLE
PS C:\> Get-bdcMicrosoftTeamsReporting | Where-Object -FilterScript {
($_.Members -eq 0) -and ($_.Guests -eq 0)
} | Select-Object -ExpandProperty DisplayName | ForEach-Object -Process {
Write-Warning -Message "Looks like $_ has no members and guests!" -ErrorAction Continue
}
Find Teams without members and guests, empty teams are boring and, more or less, useless
.EXAMPLE
PS C:\> Get-bdcMicrosoftTeamsReporting | Where-Object -FilterScript {
($_.Archived -eq $true) -and ($_.ShowInTeamsSearchAndSuggestions -eq $true)
} | Select-Object -ExpandProperty DisplayName | ForEach-Object -Process {
Write-Warning -Message "Looks like $_ is archived but searchable!" -ErrorAction Continue
}
Find archived Teams that are still searchable, might not be a bad thing...
.EXAMPLE
PS C:\> Get-bdcMicrosoftTeamsReporting -Connect -Disconnect
Get a Report for all Microsoft Teams Teams, invokes the Connect-MicrosoftTeams and Disconnect-MicrosoftTeams for you
.EXAMPLE
PS C:\> Get-bdcMicrosoftTeamsReporting -AllDetails
Get a Report for all Microsoft Teams Teams, with all the details (very verbose)
.EXAMPLE
PS C:\> Get-bdcMicrosoftTeamsReporting -GiphyDetails
Get a Report for all Microsoft Teams Teams, and include Giphy Details
.EXAMPLE
PS C:\> Get-bdcMicrosoftTeamsReporting -MemesDetails
Get a Report for all Microsoft Teams Teams, and include Memes Details
.EXAMPLE
PS C:\> Get-bdcMicrosoftTeamsReporting -GiphyDetails -MemesDetails
Get a Report for all Microsoft Teams Teams, and include Giphy and Memes Details
.EXAMPLE
PS C:\> Get-bdcMicrosoftTeamsReporting -GuestDetails
Get a Report for all Microsoft Teams Teams, and include Guest Details
.EXAMPLE
PS C:\> Get-bdcMicrosoftTeamsReporting -Detailed
Get a Report for all Microsoft Teams Teams, with more details then the regular report
.EXAMPLE
PS C:\> Get-bdcMicrosoftTeamsReporting -Detailed -GuestDetails
Get a Report for all Microsoft Teams Teams, with more details then the regular report and Guest Details
.NOTES
Reworked function to deliver everything we need to have for our Office 365 reporting service.
See the examples above and you will get an idea what you can do with filtering :-)
.LINK
https://www.powershellgallery.com/packages/MicrosoftTeams/1.0.3
.LINK
https://github.com/MicrosoftDocs/office-docs-powershell/tree/master/teams
.LINK
Get-Team
.LINK
Get-TeamUser
.LINK
Get-TeamChannel
.LINK
Connect-MicrosoftTeams
.LINK
Disconnect-MicrosoftTeams
.LINK
https://aka.ms/InstallModule
.LINK
https://github.com/tomarbuthnot/Microsoft-Teams-PowerShell
.LINK
https://opensource.org/licenses/BSD-3-Clause
#>
[CmdletBinding(ConfirmImpact = 'None')]
[OutputType([psobject])]
param
(
[Parameter(ValueFromPipeline,
ValueFromPipelineByPropertyName)]
[Alias('DoConnect')]
[switch]
$Connect,
[Parameter(ValueFromPipeline,
ValueFromPipelineByPropertyName)]
[Alias('DoDisconnect')]
[switch]
$Disconnect,
[Parameter(ValueFromPipeline,
ValueFromPipelineByPropertyName)]
[Alias('IncludeGiphyDetails', 'Giphy')]
[switch]
$GiphyDetails,
[Parameter(ValueFromPipeline,
ValueFromPipelineByPropertyName)]
[Alias('IncludeMemesDetails', 'Memes')]
[switch]
$MemesDetails,
[Parameter(ValueFromPipeline,
ValueFromPipelineByPropertyName)]
[Alias('IncludeGuestDetails', 'Guest')]
[switch]
$GuestDetails,
[Parameter(ValueFromPipeline,
ValueFromPipelineByPropertyName)]
[Alias('DetailedReport')]
[switch]
$Detailed,
[Parameter(ValueFromPipeline,
ValueFromPipelineByPropertyName)]
[Alias('VerboseReport')]
[switch]
$AllDetails
)
begin
{
#region Connect
if ($Connect)
{
# Logon
$null = (Connect-MicrosoftTeams)
}
#endregion Connect
# Crete an empty Report variable
$MicrosoftTeamsReport = @()
}
process
{
# Get all Microsoft Teams Teams and loop over them
try
{
Get-Team -ErrorAction Stop | ForEach-Object -Process {
try
{
Write-Verbose -Message ('Generate the Report for the Microsoft Teams Team {0}' -f $_.DisplayName)
# Cleanup
$TeamUserDetails = $null
# Get the User information for the Microsoft Teams Team and save it for reuse
$TeamUserDetails = $null
$TeamUserDetails = (Get-TeamUser -GroupId $_.GroupID -ErrorAction Stop)
# Get the Channel information for the Microsoft Teams Team
$TeamChannelDetails = $null
$TeamChannelDetails = ((Get-TeamChannel -GroupId $_.GroupID -ErrorAction Stop).count)
# Put all details into an object
$SingleTeamReport = (New-Object -TypeName PSobject)
#region FillSingleTeamReport
$SingleTeamReport | Add-Member -MemberType NoteProperty -Name 'DisplayName' -Value $_.DisplayName
$SingleTeamReport | Add-Member -MemberType NoteProperty -Name 'Description' -Value $_.Description
$SingleTeamReport | Add-Member -MemberType NoteProperty -Name 'Visibility' -Value $_.Visibility
$SingleTeamReport | Add-Member -MemberType NoteProperty -Name 'Archived' -Value $_.Archived
$SingleTeamReport | Add-Member -MemberType NoteProperty -Name 'ShowInTeamsSearchAndSuggestions' -Value $_.ShowInTeamsSearchAndSuggestions
$SingleTeamReport | Add-Member -MemberType NoteProperty -Name 'Channels' -Value $TeamChannelDetails
$SingleTeamReport | Add-Member -MemberType NoteProperty -Name 'Owners' -Value (($TeamUserDetails | Where-Object -FilterScript {
$_.Role -like 'owner'
}).count)
$SingleTeamReport | Add-Member -MemberType NoteProperty -Name 'Members' -Value (($TeamUserDetails | Where-Object -FilterScript {
$_.Role -like 'member'
}).count)
$SingleTeamReport | Add-Member -MemberType NoteProperty -Name 'Guests' -Value (($TeamUserDetails | Where-Object -FilterScript {
$_.Role -like 'guest'
}).count)
#region GiphyDetails
if ($GiphyDetails -or $AllDetails)
{
$SingleTeamReport | Add-Member -MemberType NoteProperty -Name 'AllowGiphy' -Value $_.AllowGiphy
$SingleTeamReport | Add-Member -MemberType NoteProperty -Name 'GiphyContentRating' -Value $_.GiphyContentRating
}
#endregion GiphyDetails
#region MemesDetails
if ($MemesDetails -or $AllDetails)
{
$SingleTeamReport | Add-Member -MemberType NoteProperty -Name 'AllowStickersAndMemes' -Value $_.AllowStickersAndMemes
$SingleTeamReport | Add-Member -MemberType NoteProperty -Name 'AllowCustomMemes' -Value $_.AllowCustomMemes
}
#endregion MemesDetails
#region GuestDetails
if ($GuestDetails -or $AllDetails)
{
$SingleTeamReport | Add-Member -MemberType NoteProperty -Name 'AllowGuestCreateUpdateChannels' -Value $_.AllowGuestCreateUpdateChannels
$SingleTeamReport | Add-Member -MemberType NoteProperty -Name 'AllowGuestDeleteChannels' -Value $_.AllowGuestDeleteChannels
}
#endregion GuestDetails
#region DetailedReport
if ($Detailed -or $AllDetails)
{
# Based on the idea of Tom Arbuthnot (https://github.com/tomarbuthnot/Microsoft-Teams-PowerShell)
$DescriptionWordCount = (($_.Description | Out-String | Measure-Object -Word).words)
$SingleTeamReport | Add-Member -MemberType NoteProperty -Name 'DescriptionWordCount' -Value $DescriptionWordCount
$SingleTeamReport | Add-Member -MemberType NoteProperty -Name 'DescriptionScore' -Value $(if ($DescriptionWordCount -eq 0)
{
'Terrible'
}
elseif ($DescriptionWordCount -le 2)
{
'Poor'
}
elseif ($DescriptionWordCount -le 5)
{
'OK'
}
elseif ($DescriptionWordCount -ge 6)
{
'Good'
}
else
{
'Unknown'
}
)
# As requested by Peter Duda
$SingleTeamReport | Add-Member -MemberType NoteProperty -Name 'Classification' -Value $(if ($_.Classification)
{
$_.Classification
}
else
{
'None'
}
)
# New since 2020/01
$SingleTeamReport | Add-Member -MemberType NoteProperty -Name 'MailNickName' -Value $(if ($_.MailNickName)
{
$_.MailNickName
}
else
{
'None'
}
)
# Verbose reporting
$SingleTeamReport | Add-Member -MemberType NoteProperty -Name 'AllowCreateUpdateChannels' -Value $_.AllowCreateUpdateChannels
$SingleTeamReport | Add-Member -MemberType NoteProperty -Name 'AllowDeleteChannels' -Value $_.AllowDeleteChannels
$SingleTeamReport | Add-Member -MemberType NoteProperty -Name 'AllowAddRemoveApps' -Value $_.AllowAddRemoveApps
$SingleTeamReport | Add-Member -MemberType NoteProperty -Name 'AllowCreateUpdateRemoveTabs' -Value $_.AllowCreateUpdateRemoveTabs
$SingleTeamReport | Add-Member -MemberType NoteProperty -Name 'AllowCreateUpdateRemoveConnectors' -Value $_.AllowCreateUpdateRemoveConnectors
$SingleTeamReport | Add-Member -MemberType NoteProperty -Name 'AllowUserEditMessages' -Value $_.AllowUserEditMessages
$SingleTeamReport | Add-Member -MemberType NoteProperty -Name 'AllowUserDeleteMessages' -Value $_.AllowUserDeleteMessages
$SingleTeamReport | Add-Member -MemberType NoteProperty -Name 'AllowOwnerDeleteMessages' -Value $_.AllowOwnerDeleteMessages
$SingleTeamReport | Add-Member -MemberType NoteProperty -Name 'AllowTeamMentions' -Value $_.AllowTeamMentions
$SingleTeamReport | Add-Member -MemberType NoteProperty -Name 'AllowChannelMentions' -Value $_.AllowChannelMentions
}
#endregion DetailedReport
#region FinalValue
$SingleTeamReport | Add-Member -MemberType NoteProperty -Name 'GroupId' -Value $_.GroupId
#endregion FinalValue
#endregion FillSingleTeamReport
# Append to the Report
$MicrosoftTeamsReport += $SingleTeamReport
}
catch
{
#region WarningHandler
# get error record
[Management.Automation.ErrorRecord]$e = $_
# retrieve information about runtime error
$info = [PSCustomObject]@{
Exception = $e.Exception.Message
Reason = $e.CategoryInfo.Reason
Target = $e.CategoryInfo.TargetName
Script = $e.InvocationInfo.ScriptName
Line = $e.InvocationInfo.ScriptLineNumber
Column = $e.InvocationInfo.OffsetInLine
}
# output information. Post-process collected info, and log info (optional)
$info | Out-String | Write-Verbose
$paramWriteWarning = @{
Message = $e.Exception.Message
ErrorAction = 'Continue'
WarningAction = 'Continue'
}
Write-Warning @paramWriteWarning
#region WarningHandler
}
}
}
catch
{
#region ErrorHandler
# get error record
[Management.Automation.ErrorRecord]$e = $_
# retrieve information about runtime error
$info = [PSCustomObject]@{
Exception = $e.Exception.Message
Reason = $e.CategoryInfo.Reason
Target = $e.CategoryInfo.TargetName
Script = $e.InvocationInfo.ScriptName
Line = $e.InvocationInfo.ScriptLineNumber
Column = $e.InvocationInfo.OffsetInLine
}
# output information. Post-process collected info, and log info (optional)
$info | Out-String | Write-Verbose
$paramWriteError = @{
Message = $e.Exception.Message
ErrorAction = 'Stop'
Exception = $e.Exception
TargetObject = $e.CategoryInfo.TargetName
}
Write-Error @paramWriteError
# Just in case
Exit 1
#region ErrorHandler
}
}
end
{
#region Disconnect
if ($Disconnect)
{
# Logoff
$null = (Disconnect-MicrosoftTeams -Confirm:$false)
}
#endregion Disconnect
#region ShowReport
# Dump the Report
$MicrosoftTeamsReport
#endregion ShowReport
}
}
#region LICENSE
<#
BSD 3-Clause License
Copyright (c) 2021, enabling Technology
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#>
#endregion LICENSE
#region DISCLAIMER
<#
DISCLAIMER:
- Use at your own risk, etc.
- This is open-source software, if you find an issue try to fix it yourself. There is no support and/or warranty in any kind
- This is a third-party Software
- The developer of this Software is NOT sponsored by or affiliated with Microsoft Corp (MSFT) or any of its subsidiaries in any way
- The Software is not supported by Microsoft Corp (MSFT)
- By using the Software, you agree to the License, Terms, and any Conditions declared and described above
- If you disagree with any of the terms, and any conditions declared: Just delete it and build your own solution
#>
#endregion DISCLAIMER

View File

@@ -0,0 +1,300 @@
#requires -Version 3.0 -Modules BitsTransfer -RunAsAdministrator
<#
.SYNOPSIS
Download, install, and Tweak System and Apps for Terminal Server use
.DESCRIPTION
Download, install, and Tweak System and Apps for Terminal Server (WVD/VDI/WDS) use
.NOTES
Early testing release - Future releases might get some parameters
Changelog:
1.0.0: Initial Release
Version 1.0.0
.LINK
http://enatec.io
#>
[CmdletBinding(ConfirmImpact = 'Low',
SupportsShouldProcess)]
param ()
begin
{
Write-Output -InputObject 'Download, install, and Tweak System and Apps for Terminal Server use'
# Default URL (Assume we use 64Bit)
[string]$FSLogixUrl = 'https://aka.ms/fslogix_download'
#region PossibleParameters
# Where to Store it
[string]$Target = ($env:Temp)
# File Name
[string]$TargetName = 'fslogix.zip'
# Install Switch
[string]$Arguments = '/install /quiet /norestart'
#endregion PossibleParameters
#region Defaults
# Set the full path of the downloaded installer
[string]$InstallerPackage = ($Target + '\' + $TargetName)
[string]$InstallerDestination = (($InstallerPackage).Replace('.zip', ''))
[string]$InstallerExecutable = ($InstallerDestination + '\x64\Release\FSLogixAppsSetup.exe')
$SCT = 'SilentlyContinue'
$STP = 'Stop'
#endregion Defaults
}
process
{
Write-Verbose -Message ('Downloading {0} to {1}' -f $TargetName, $InstallerPackage)
# Use BitsTransfer to download the latest installer
$paramStartBitsTransfer = @{
Source = $FSLogixUrl
Destination = $InstallerPackage
Priority = 'High'
TransferPolicy = 'Always'
ErrorAction = $STP
}
$null = (Start-BitsTransfer @paramStartBitsTransfer)
# Expand FSLogix Installer
$paramTestPath = @{
Path = $InstallerPackage
ErrorAction = $SCT
}
if (Test-Path @paramTestPath)
{
$paramTestPath = @{
Path = $InstallerDestination
ErrorAction = $SCT
}
if (-not (Test-Path @paramTestPath))
{
$paramNewItem = @{
Path = $InstallerDestination
Force = $true
Confirm = $false
ItemType = 'Directory'
ErrorAction = $SCT
}
$null = (New-Item @paramNewItem)
}
try
{
# Expand-Archive is to buggy!
$null = (Add-Type -AssemblyName System.IO.Compression.FileSystem -ErrorAction $STP)
$null = ([IO.Compression.ZipFile]::ExtractToDirectory($InstallerPackage, $InstallerDestination))
}
catch
{
# OK! That is crappy, but it still works well as a fallback.
$shellApp = (New-Object -ComObject Shell.Application -ErrorAction $STP)
$shellZip = $shellApp.NameSpace([String]$InstallerPackage)
$shellDest = $shellApp.NameSpace($InstallerDestination)
$shellDest.CopyHere($shellZip.items())
}
}
else
{
# get error record
[Management.Automation.ErrorRecord]$e = $_
# retrieve information about runtime error
$info = [PSCustomObject]@{
Exception = $e.Exception.Message
Reason = $e.CategoryInfo.Reason
Target = $e.CategoryInfo.TargetName
Script = $e.InvocationInfo.ScriptName
Line = $e.InvocationInfo.ScriptLineNumber
Column = $e.InvocationInfo.OffsetInLine
}
# output information. Post-process collected info, and log info (optional)
$info | Out-String | Write-Verbose
$paramWriteError = @{
Message = $e.Exception.Message
ErrorAction = $STP
Exception = $e.Exception
TargetObject = $e.CategoryInfo.TargetName
}
Write-Error @paramWriteError
# We are done
break
}
# Install FSLogix
$paramTestPath = @{
Path = $InstallerExecutable
ErrorAction = $SCT
}
if (Test-Path @paramTestPath)
{
$paramGetItemProperty = @{
Path = $InstallerExecutable
ErrorAction = $SCT
}
$InstallerVersion = ((Get-ItemProperty @paramGetItemProperty).VersionInfo.ProductVersion)
Write-Verbose -Message ('Running FSLogix installer version {0}' -f $InstallerVersion)
$paramStartProcess = @{
FilePath = $InstallerExecutable
ArgumentList = $Arguments
Wait = $true
PassThru = $true
ErrorAction = $STP
}
$InstallerProcess = (Start-Process @paramStartProcess)
if (($InstallerProcess | Select-Object -ExpandProperty ExitCode) -eq 0)
{
Write-Verbose -Message ('Installed FSLogix version {0}' -f $InstallerVersion)
}
else
{
Write-Warning -Message ('Installer exit code {0}.' -f $InstallerProcess.ExitCode)
}
Write-Verbose -Message ('Removing file: {0}' -f $InstallerPackage)
# Remove the downloaded Installaer Package
$paramRemoveItem = @{
Path = $InstallerPackage
Confirm = $false
Force = $true
ErrorAction = $SCT
}
$null = (Remove-Item @paramRemoveItem)
# Install the expanded stuff
$paramRemoveItem = @{
Path = $InstallerDestination
Recurse = $true
Confirm = $false
Force = $true
ErrorAction = $SCT
}
$null = (Remove-Item @paramRemoveItem)
# Legacy HKLM Path for WVD/VDI/WDS Environment
$paramNewItem = @{
Path = 'HKLM:\SOFTWARE\Citrix\PortICA'
Confirm = $false
Force = $true
ErrorAction = $SCT
}
$null = (New-Item @paramNewItem)
# Ensure that the registry path exists
$paramNewItem = @{
Path = 'HKLM:\SOFTWARE\Microsoft\Teams'
Confirm = $false
Force = $true
ErrorAction = $SCT
}
$null = (New-Item @paramNewItem)
# Tell Microsoft Teams that it runs in an WVD/VDI/WDS Environment
# Source: https://docs.microsoft.com/en-us/azure/virtual-desktop/teams-on-wvd
$paramNewItemProperty = @{
Path = 'HKLM:\SOFTWARE\Microsoft\Teams'
Name = 'IsWVDEnvironment'
PropertyType = 'DWORD'
Value = 1
Confirm = $false
Force = $true
ErrorAction = $SCT
}
$null = (New-ItemProperty @paramNewItemProperty)
# Ensure that the registry path exists
$paramNewItem = @{
Path = 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\StartupApproved\Run32'
Confirm = $false
Force = $true
ErrorAction = $SCT
}
$null = (New-Item @paramNewItem)
# Do not start Microsoft Teams after Login
$paramNewItemProperty = @{
Path = 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\StartupApproved\Run32'
Name = 'Teams'
PropertyType = 'Binary'
Value = ([byte[]](0x01, 0x00, 0x00, 0x00, 0x1a, 0x19, 0xc3, 0xb9, 0x62, 0x69, 0xd5, 0x01))
Confirm = $false
Force = $true
ErrorAction = $SCT
}
$null = (New-ItemProperty @paramNewItemProperty)
}
else
{
# get error record
[Management.Automation.ErrorRecord]$e = $_
# retrieve information about runtime error
$info = [PSCustomObject]@{
Exception = $e.Exception.Message
Reason = $e.CategoryInfo.Reason
Target = $e.CategoryInfo.TargetName
Script = $e.InvocationInfo.ScriptName
Line = $e.InvocationInfo.ScriptLineNumber
Column = $e.InvocationInfo.OffsetInLine
}
# output information. Post-process collected info, and log info (optional)
$info | Out-String | Write-Verbose
$paramWriteError = @{
Message = $e.Exception.Message
ErrorAction = $STP
Exception = $e.Exception
TargetObject = $e.CategoryInfo.TargetName
}
Write-Error @paramWriteError
# We are done
break
}
}
#region LICENSE
<#
BSD 3-Clause License
Copyright (c) 2021, enabling Technology
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#>
#endregion LICENSE
#region DISCLAIMER
<#
DISCLAIMER:
- Use at your own risk, etc.
- This is open-source software, if you find an issue try to fix it yourself. There is no support and/or warranty in any kind
- This is a third-party Software
- The developer of this Software is NOT sponsored by or affiliated with Microsoft Corp (MSFT) or any of its subsidiaries in any way
- The Software is not supported by Microsoft Corp (MSFT)
- By using the Software, you agree to the License, Terms, and any Conditions declared and described above
- If you disagree with any of the terms, and any conditions declared: Just delete it and build your own solution
#>
#endregion DISCLAIMER

View File

@@ -0,0 +1,137 @@
#requires -Version 3.0 -Modules NetSecurity -RunAsAdministrator
<#
.SYNOPSIS
Tweak the Firewall Rules for Microsoft Teams clients
.DESCRIPTION
Tweak the Firewall Rules for Microsoft Teams clients for all users that have it installed
.NOTES
Early testing release
Changelog:
1.0.0: Initial Release
Version 1.0.0
.LINK
http://enatec.io
#>
[CmdletBinding(ConfirmImpact = 'Low',
SupportsShouldProcess)]
param ()
begin
{
Write-Output -InputObject 'Tweak the Firewall Rules for Microsoft Teams clients for all users that have it installed'
#region Defaults
$SCT = 'SilentlyContinue'
#endregion Defaults
}
process
{
# Creates firewall rules for Microsoft Teams
$AllUsers = $null
$paramJoinPath = @{
Path = $env:SystemDrive
ChildPath = 'Users'
ErrorAction = $SCT
}
$paramGetChildItem = @{
Path = (Join-Path @paramJoinPath)
ErrorAction = $SCT
Exclude = 'Public', 'ADMINI~*'
}
$AllUsers = (Get-ChildItem @paramGetChildItem)
if ($null -ne $AllUsers)
{
foreach ($SingleUser in $AllUsers)
{
# Cleanup
$FullTeamsPath = $null
# get the Executable
$paramJoinPath = @{
Path = $SingleUser.FullName
ChildPath = 'AppData\Local\Microsoft\Teams\Current\Teams.exe'
ErrorAction = $SCT
}
$FullTeamsPath = (Join-Path @paramJoinPath)
$paramTestPath = @{
Path = $FullTeamsPath
ErrorAction = $SCT
}
if (Test-Path @paramTestPath)
{
$paramGetNetFirewallApplicationFilter = @{
Program = $FullTeamsPath
ErrorAction = $SCT
}
if (-not (Get-NetFirewallApplicationFilter @paramGetNetFirewallApplicationFilter))
{
# Cleanup
$NetFirewallRuleName = $null
# Apply the Rulename
$NetFirewallRuleName = ('Teams.exe for user {0}' -f $SingleUser.Name)
'UDP', 'TCP' | ForEach-Object -Process {
$paramNewNetFirewallRule = @{
DisplayName = $NetFirewallRuleName
Direction = 'Inbound'
Profile = 'Any'
Program = $FullTeamsPath
Action = 'Allow'
Protocol = $_
Enabled = 'True'
Confirm = $false
ErrorAction = $SCT
}
$null = (New-NetFirewallRule @paramNewNetFirewallRule)
}
# Cleanup
$NetFirewallRuleName = $null
}
}
# Cleanup
$FullTeamsPath = $null
}
}
}
#region LICENSE
<#
BSD 3-Clause License
Copyright (c) 2021, enabling Technology
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#>
#endregion LICENSE
#region DISCLAIMER
<#
DISCLAIMER:
- Use at your own risk, etc.
- This is open-source software, if you find an issue try to fix it yourself. There is no support and/or warranty in any kind
- This is a third-party Software
- The developer of this Software is NOT sponsored by or affiliated with Microsoft Corp (MSFT) or any of its subsidiaries in any way
- The Software is not supported by Microsoft Corp (MSFT)
- By using the Software, you agree to the License, Terms, and any Conditions declared and described above
- If you disagree with any of the terms, and any conditions declared: Just delete it and build your own solution
#>
#endregion DISCLAIMER

View File

@@ -0,0 +1,184 @@
function Invoke-mtrDisableModernAuthentication
{
<#
.SYNOPSIS
Disable Modern Authentication for a Microsoft Teams Room Device Account
.DESCRIPTION
Disable Modern Authentication for a Microsoft Teams Room Device Account
It dsables it in Exchange Online and Skype for Business Online. It also configures the tenant to do so, if needed.
.PARAMETER Identity
The Microsoft Teams Rooms (MTR) Account Search String
.EXAMPLE
PS C:\> .\Invoke-mtrDisableModernAuthentication.ps1 -Identity 'MyTeamRoom'
.EXAMPLE
PS C:\> .\Invoke-mtrDisableModernAuthentication.ps1 -Identity 'TeamRoom@contoso.com'
.NOTES
Just a quick and dirty tool to do the job, nothing fancy and without a real error handling!
-> Use at your own risk!
You need to be a tenant admin to configure all the things
#>
[CmdletBinding(ConfirmImpact = 'Low')]
param
(
[Parameter(Mandatory, HelpMessage = 'The Microsoft Teams Rooms (MTR) Account Search String',
ValueFromPipeline,
ValueFromPipelineByPropertyName,
Position = 0)]
[ValidateNotNullOrEmpty()]
[Alias('SearchString', 'mtrAccount')]
[string]
$Identity
)
begin
{
#region Defaults
$STP = 'Stop'
$SCT = 'SilentlyContinue'
#endregion Defaults
#region GeneralParameters
$RemovePSSessionDefaultParams = @{
Confirm = $false
ErrorAction = $SCT
}
$RemoveModuleDefaultParams = @{
Force = $true
Confirm = $false
ErrorAction = $SCT
}
#endregion GeneralParameters
Write-Verbose -Message 'Message'
}
process
{
#region ConnectAzureAD
$null = (Connect-AzureAD)
#endregion ConnectAzureAD
#region ConnectSkypeForBusinessOnline
# We use a crappy workaround, because the Modern Auth window never shows up to querry the admin UPN, and I do NOT trust the command to querry it
$SkypeForBusinessSession = (New-CsOnlineSession -UserName (Read-Host -Prompt 'Please enter the admin principal name (ex. admin@contoso.com)'))
$paramImportPSSession = @{
Session = $SkypeForBusinessSession
DisableNameChecking = $true
AllowClobber = $true
}
$null = (Import-PSSession @paramImportPSSession)
#endregion ConnectSkypeForBusinessOnline
#region ConnectExchangeOnline
# We use the ExchangeOnlineShell Module from the Gallery
if (-not (Get-Command -Name Get-Mailbox -ErrorAction $SCT))
{
$paramConnectExchangeOnlineShell = @{
Confirm = $false
WarningAction = $SCT
ErrorAction = $STP
}
$null = (Connect-ExchangeOnlineShell @paramConnectExchangeOnlineShell)
}
#endregion ConnectExchangeOnline
#region CheckModernAuth
# Do we have Modern Auth enabled Global?
if ((Get-OrganizationConfig | Select-Object -ExpandProperty OAuth2ClientProfileEnabled) -eq $true)
{
# Disconnect Modern Authentication (For a single user) - In this case the MTR
$paramRevokeAzureADUserAllRefreshToken = @{
ObjectId = (Get-AzureADUser -SearchString $Identity | Select-Object -ExpandProperty objectId)
ErrorAction = $SCT
}
$null = (Revoke-AzureADUserAllRefreshToken @paramRevokeAzureADUserAllRefreshToken)
$null = (Revoke-AzureADUserAllRefreshToken @paramRevokeAzureADUserAllRefreshToken)
# Allow non Modern Auth in Skype for Business
if ((Get-CsOAuthConfiguration -ErrorAction $SCT | Select-Object -ExpandProperty ClientAdalAuthOverride) -ne 'Allowed')
{
$paramSetCsOAuthConfiguration = @{
ClientAdalAuthOverride = 'Allowed'
Confirm = $false
ErrorAction = $SCT
}
$null = (Set-CsOAuthConfiguration @paramSetCsOAuthConfiguration)
}
}
else
{
# Shame on you!
Write-Warning -Message 'Looks like Modern Auth is not enabled for this tenant!' -WarningAction $STP
}
#endregion CheckModernAuth
#region DisconnectAzureAD
$null = (Disconnect-AzureAD -Confirm:$false -ErrorAction $SCT)
#endregion DisconnectAzureAD
#region DisconnectSkypeForBusiness
$paramRemoveModule = @{
Name = (Get-Command -Name Set-CsOAuthConfiguration -ErrorAction $SCT | Select-Object -ExpandProperty Source)
Force = $true
ErrorAction = $SCT
}
$null = (Remove-Module @paramRemoveModule)
$null = ($SkypeForBusinessSession.Id | Remove-PSSession @RemovePSSessionDefaultParams)
#endregion DisconnectSkypeForBusiness
#region DisconnectExchangeOnline
$ExchangeSessionID = (Get-PSSession | Where-Object {
$_.ComputerName -eq 'outlook.office365.com'
} | Select-Object -ExpandProperty Id)
if ($ExchangeSessionID)
{
$paramDisconnectExchangeOnlineShell = @{
SessionID = $ExchangeSessionID
Confirm = $false
}
$null = (Disconnect-ExchangeOnlineShell @paramDisconnectExchangeOnlineShell)
}
# Will be removed soon (Disconnect-ExchangeOnlineShell will handle this for us!)
$RemoveModuleName = (Get-Command -Name Get-OrganizationConfig -ErrorAction $SCT | Select-Object -ExpandProperty Source)
if ($RemoveModuleName)
{
$paramRemoveModule = @{
Name = $RemoveModuleName
Force = $true
ErrorAction = $SCT
}
$null = (Remove-Module @RemoveModuleDefaultParams)
}
#endregion DisconnectExchangeOnline
}
end
{
#region FinalCleanup
# Just in case: We remove all sessions that might still be around
$null = ((Get-PSSession -ErrorAction $SCT | Where-Object {
$_.ComputerName -eq 'outlook.office365.com'
}) | Remove-PSSession @RemovePSSessionDefaultParams)
$null = ((Get-PSSession -ErrorAction $SCT | Where-Object {
$_.ComputerName -like 'admin*.online.lync.com'
}) | Remove-PSSession @RemovePSSessionDefaultParams)
# Remove the Modules (Here just in case we missed something above)
$null = (Remove-Module -Name (Get-Command -Name Connect-ExchangeOnlineShell -ErrorAction $SCT | Select-Object -ExpandProperty Source) @RemoveModuleDefaultParams)
$null = (Remove-Module -Name (Get-Command -Name Disconnect-AzureAD -ErrorAction $SCT | Select-Object -ExpandProperty Source) @RemoveModuleDefaultParams)
$null = (Remove-Module -Name (Get-Command -Name New-CsOnlineSession -ErrorAction $SCT | Select-Object -ExpandProperty Source) @RemoveModuleDefaultParams)
#endregion FinalCleanup
}
}

View File

@@ -0,0 +1,29 @@
BSD 3-Clause License
Copyright (c) 2021, enabling Technology <http://enatec.io>
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@@ -0,0 +1,448 @@
#requires -Version 3.0
<#
.SYNOPSIS
Creates a new Microsoft Teams team with the MicrosoftTeams Module.
.DESCRIPTION
Creates a new Microsoft Teams team with the MicrosoftTeams Module.
The new team will be backed by a newly created unified group and SharePoint Online Site.
The script depends on Microsoft's Version 0.9.6 of the MicrosoftTeams Module.
Please note: Not all authentications methods of the latest MicrosoftTeams Module are supported!
.PARAMETER msTeamsCreds
Specifies a PSCredential object. For more information about the PSCredential object, type Get-Help Get-Credential.
The PSCredential object provides the user ID and password for organizational ID credentials.
.PARAMETER mfa
Use the web based authentication. Supports MFA and prevents issues in non ADFS implementations.
.PARAMETER DisplayName
Todeam display name. Team Name Characters Limit is 256.
.PARAMETER Alias
Same as displayName without any spaces. Team Alias Characters Limit is 64
.PARAMETER Description
Team description. Team Description Characters Limit is 1024.
.PARAMETER AccessType
Team access type. Valid values are "Private" and "Public". Default is "Private". (This parameter has the same meaning as -AccessType in New-UnifiedGroup.)
.PARAMETER AddCreatorAsMember
This setting lets you decide if you will be added as a member of the team. The default is false.
.PARAMETER Owner
UPN/Mail of the Teams Owner, multiple values are supported!
.PARAMETER User
member Users for the Group, Please use UPN or Mail.
Multiple values are supported.
.EXAMPLE
PS C:\> .\New-AITMicrosoftTeams -mfa -DisplayName 'Contoso Support'
Creates the Microsoft Team 'Contoso Support'. Uses Weblog (supports MFA)
.EXAMPLE
PS C:\> .\New-AITMicrosoftTeams -msTeamsCreds $O365 -DisplayName 'Contoso Support'
Creates the Microsoft Team 'Contoso Support'. Uses existing credentials stored in the variable $O365 to authenticate.
This might be the perfect way for automation, but use stored credentials might also be insecure.
.EXAMPLE
PS C:\> .\New-AITMicrosoftTeams -DisplayName 'Contoso Support'
Creates the Microsoft Team 'Contoso Support'
.EXAMPLE
PS C:\> .\New-AITMicrosoftTeams -DisplayName 'Contoso Support' -Alias 'AITSupport'
Creates the Microsoft Team 'Contoso Support' with an Alias 'AITSupport'
.EXAMPLE
PS C:\> .\New-AITMicrosoftTeams -DisplayName 'Contoso Info-pool' -AccessType 'public'
Creates the Microsoft Team 'Contoso Info-pool', public mean open to join for every member of the organization.
.EXAMPLE
PS C:\> .\New-AITMicrosoftTeams -DisplayName 'Contoso Development' -Description 'Contoso IT Development Team' -Owner 'john.doe@acontoso.com'
Creates the Microsoft Team 'Contoso Development', sets a description and add 'john.doe@contoso.com' as Owner.
.EXAMPLE
PS C:\> .\New-AITMicrosoftTeams -DisplayName 'Contoso Core Dev' -AddCreatorAsMember $true
Creates the Microsoft Team 'Contoso Core Dev' and adds the creator to the new Team.
.EXAMPLE
PS C:\> Install-Module -Name MicrosoftTeams
Install the dependency Module from Microsoft via PowerShellGet.
.NOTES
Releasenotes:
1.0.3 2019-04-26: Fix Module Statement to use the correct version (0.9.6) to avoid issues with our workaround.
1.0.2 2019-02-05: Reintroduce the -MFA switch to support the web based authentication. Prevent issues in non ADFS implementations.
1.0.1 2019-02-04: Add workaround for AddCreatorAsMember Bug (Creator is added as owner all the time)
1.0.0 2018-12-31: Internal Release
THIS CODE IS MADE AVAILABLE AS IS, WITHOUT WARRANTY OF ANY KIND. THE ENTIRE RISK OF THE USE OR THE RESULTS FROM THE USE OF THIS CODE REMAINS WITH THE USER.
Dependencies:
The script depends on Microsoft's Version 0.9.6 of the MicrosoftTeams PowerShell Module.
The MicrosoftTeams PowerShell Module GA Version (1.0.0) is not yet tested!
Install it with PowerShellGet:
PS C:\> Install-Module -Name MicrosoftTeams -RequiredVersion 0.9.6
.LINK
https://www.powershellgallery.com/packages/MicrosoftTeams/0.9.6
.LINK
https://aka.ms/InstallModule
#>
[CmdletBinding(DefaultParameterSetName = 'MFA',
ConfirmImpact = 'None')]
param
(
[Parameter(ParameterSetName = 'Credentials',
ValueFromPipeline,
ValueFromPipelineByPropertyName,
Position = 0)]
[System.Management.Automation.Credential()]
[Alias('TeamsCredentials', 'TeamsAdminCredentials', 'Office365creds')]
[pscredential]
$msTeamsCreds,
[Parameter(ParameterSetName = 'MFA',
ValueFromPipeline,
ValueFromPipelineByPropertyName,
Position = 0)]
[Alias('UseMFA')]
[switch]
$mfa,
[Parameter(Mandatory,
ValueFromPipeline,
ValueFromPipelineByPropertyName,
Position = 1,
HelpMessage = 'Team display name.')]
[ValidateNotNullOrEmpty()]
[Alias('TeamsDisplayName')]
[string]
$DisplayName,
[Parameter(ValueFromPipeline,
ValueFromPipelineByPropertyName,
Position = 2)]
[Alias('TeamsAlias')]
[string]
$Alias,
[Parameter(ValueFromPipeline,
ValueFromPipelineByPropertyName,
Position = 3)]
[Alias('TeamsDescription')]
[string]
$Description,
[Parameter(ValueFromPipeline,
ValueFromPipelineByPropertyName,
Position = 4)]
[ValidateSet('HiddenMembership', 'Private', 'Public', IgnoreCase = $true)]
[Alias('TeamsAccessType')]
[string]
$AccessType = 'Private',
[Parameter(ValueFromPipeline,
ValueFromPipelineByPropertyName,
Position = 5)]
[Alias('AddCreatorAsTeamsMember')]
[switch]
$AddCreatorAsMember = $false,
[Parameter(ValueFromPipeline,
ValueFromPipelineByPropertyName,
Position = 6)]
[Alias('TeamsOwner')]
[string[]]
$Owner,
[Parameter(ValueFromPipeline,
ValueFromPipelineByPropertyName,
Position = 7)]
[Alias('TeamsUser', 'TeamsMember')]
[string[]]
$User
)
begin
{
#region VersionRequirement
try
{
$paramRemoveModule = @{
Name = 'MicrosoftTeams'
Force = $true
ErrorAction = 'SilentlyContinue'
WarningAction = 'SilentlyContinue'
}
$null = (Remove-Module @paramRemoveModule)
$paramImportModule = @{
Name = 'MicrosoftTeams'
MaximumVersion = '0.9.6'
Force = $true
ErrorAction = 'Stop'
WarningAction = 'SilentlyContinue'
}
$null = (Import-Module @paramImportModule)
}
catch
{
$paramWriteError = @{
Message = 'Microsoft´s Version 0.9.6 of the MicrosoftTeams PowerShell Module'
ErrorAction = 'Stop'
Category = 'NotInstalled'
Exception = 'Required Module not found'
RecommendedAction = 'Please install Version 0.9.6 of the MicrosoftTeams PowerShell Module via Install-Module -Name MicrosoftTeams -RequiredVersion 0.9.6'
}
Write-Error @paramWriteError
# Only here to catch a global ErrorAction overwrite
break
}
#endregion VersionRequirement
#region AuthChecker
if (($msTeamsCreds) -and ($mfa))
{
$paramWriteError = @{
Message = 'You have selected muliple authentication methods. This is not valid'
Exception = 'Muliple authentication methods selected'
Category = 'AuthenticationError'
ErrorAction = 'Stop'
}
Write-Error @paramWriteError
# Only here to catch a global ErrorAction overwrite
break
}
#endregion AuthChecker
#region Defaults
#region AccessType
if (-not ($AccessType))
{
$AccessType = 'Private'
}
#endregion AccessType
#region AddCreatorAsMember
if (-not ($AddCreatorAsMember))
{
$AddCreatorAsMember = $false
}
#endregion AddCreatorAsMember
#endregion defaults
}
process
{
if ($pscmdlet.ShouldProcess($DisplayName, 'Create'))
{
try
{
#region Authentication
if (-not ($mfa))
{
#region CredentialHandler
if (-not ($msTeamsCreds))
{
# Get the credentials / Use it within the script only
$script:msTeamsCreds = (Get-Credential -Message 'Please use credentials with Teams Admin capabilities.')
}
#endregion CredentialHandler
#region ConnectMicrosoftTeams
$paramConnectMicrosoftTeams = @{
Credential = $msTeamsCreds
Confirm = $false
ErrorAction = 'Stop'
}
$null = (Connect-MicrosoftTeams @paramConnectMicrosoftTeams)
#endregion ConnectMicrosoftTeams
}
else
{
#region ConnectMicrosoftTeams
# Use the Web login
$paramConnectMicrosoftTeams = @{
Confirm = $false
ErrorAction = 'Stop'
}
$null = (Connect-MicrosoftTeams @paramConnectMicrosoftTeams)
#endregion ConnectMicrosoftTeams
}
#endregion Authentication
#region NewTeam
#region SplatDefaults
$paramNewTeam = @{
DisplayName = $DisplayName
Visibility = $AccessType
ErrorAction = 'Stop'
}
#endregion SplatDefaults
#region Optionals
if (($msTeamsCreds.UserName) -and ($AddCreatorAsMember -eq $true))
{
$paramNewTeam | Add-Member -MemberType NoteProperty -Name Owner -Value $msTeamsCreds.UserName
}
if ($Alias)
{
$paramNewTeam | Add-Member -MemberType NoteProperty -Name Alias -Value $Alias
}
if ($Description)
{
$paramNewTeam | Add-Member -MemberType NoteProperty -Name Description -Value $Description
}
#region Optionals
#region CreateTeam
$NewTeam = (New-Team @paramNewTeam)
#endregion CreateTeam
if (-not ($NewTeam.GroupId))
{
Write-Error -Message ('Error while try to create {0}' -f $DisplayName)
}
else
{
Write-Verbose -Message "The new Team id is $($NewTeam.GroupId)"
#region BugWorkAround
#BUG: There is a bug in the AddCreatorAsMember implemntation of Microsoft
if ($AddCreatorAsMember -eq $false)
{
Write-Verbose -Message 'Workaround: Workaround for the AddCreatorAsMember of the Microsoft MicrosoftTeams Module'
Remove-TeamUser -GroupId $NewTeam.GroupId -User $msTeamsCreds.UserName -ErrorAction SilentlyContinue
}
#endregion BugWorkAround
#region SetOwner
if ($Owner)
{
foreach ($Admin in $Owner)
{
try
{
$paramAddTeamUser = @{
GroupId = $NewTeam.GroupId
User = $Admin
Role = 'Owner'
ErrorAction = 'Stop'
}
$null = (Add-TeamUser @paramAddTeamUser)
}
catch
{
Write-Warning -Message ('Unable to add {0} as owner to the Team {1}' -f $Admin, $DisplayName)
}
}
}
else
{
Write-Warning -Message ('The Team {0} has no owner!' -f $DisplayName)
}
#endregion SetOwner
#region Setmember
if ($User)
{
foreach ($Member in $User)
{
try
{
$paramAddTeamUser = @{
GroupId = $NewTeam.GroupId
User = $Member
Role = 'Member'
ErrorAction = 'Stop'
}
$null = (Add-TeamUser @paramAddTeamUser)
}
catch
{
Write-Warning -Message ('Unable to add {0} as member to the Team {1}' -f $Member, $DisplayName)
}
}
}
#endregion Setmember
}
#endregion NewTeam
}
catch
{
#region ErrorHandler
# get error record
[Management.Automation.ErrorRecord]$e = $_
# retrieve information about runtime error
$info = [PSCustomObject]@{
Exception = $e.Exception.Message
Reason = $e.CategoryInfo.Reason
Target = $e.CategoryInfo.TargetName
Script = $e.InvocationInfo.ScriptName
Line = $e.InvocationInfo.ScriptLineNumber
Column = $e.InvocationInfo.OffsetInLine
}
$info | Out-String | Write-Verbose
Write-Error -Message ($info.Exception) -ErrorAction Stop
# Only here to catch a global ErrorAction overwrite
break
#endregion ErrorHandler
}
finally
{
#region Cleanup
$null = (Disconnect-MicrosoftTeams -Confirm:$false)
#endregion Cleanup
}
}
}
end
{
Write-Verbose -Message ('Created the Team {0}' -f $DisplayName)
}
#region LICENSE
<#
BSD 3-Clause License
Copyright (c) 2021, enabling Technology
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#>
#endregion LICENSE
#region DISCLAIMER
<#
DISCLAIMER:
- Use at your own risk, etc.
- This is open-source software, if you find an issue try to fix it yourself. There is no support and/or warranty in any kind
- This is a third-party Software
- The developer of this Software is NOT sponsored by or affiliated with Microsoft Corp (MSFT) or any of its subsidiaries in any way
- The Software is not supported by Microsoft Corp (MSFT)
- By using the Software, you agree to the License, Terms, and any Conditions declared and described above
- If you disagree with any of the terms, and any conditions declared: Just delete it and build your own solution
#>
#endregion DISCLAIMER

View File

@@ -0,0 +1,719 @@
<#
.SYNOPSIS
Create a Project team in Teams with folder structure in files tab
.TeamsDescription
Create a Project team in Teams with folder structure in files tab
Based on Alexander Holmeset's version.
.DESCRIPTION
A detailed description of the file.
.PARAMETER TeamName
Name of the Microsoft Teams team
.PARAMETER TeamsOwner
TeamsOwner of the new Microsoft Teams team
.PARAMETER privatepublic
Os it a private or Public team?
.PARAMETER TeamsDescription
The TeamsDescription for the new team
.PARAMETER ClientId
Azure AD Application (client) ID
.PARAMETER TenantId
Azure AD Tenant ID
.PARAMETER ClientSecret
Azure AD secret
.PARAMETER TenantName
Office 365 Tenant Name (e.g. contoso for https://contoso.sharepoint.com)
.PARAMETER DocumentLibrary
Document Library Folder, default is /shared documents
.EXAMPLE
PS C:\> .\ProjectTeamStructure.ps1 -TeamName 'Value1' -TeamsOwner 'Value2'
.NOTES
Original found in Alexander Holmeset's Blog
My version starts to make it a bit more flexible (e.g. more parameters)
I might update this to be more configurable in the future
.LINK
https://alexholmeset.blog/2019/05/01/project-team-in-teams-with-folder-structure-in-files-tab/
.LINK
https://gist.githubusercontent.com/AlexanderHolmeset/d447cd7c24dd91c3275ad17a5091f0ed/raw/79478f1e789ab36a9236f21a3161685091b12e62/ProjectTeamStructure.ps1
#>
[CmdletBinding(ConfirmImpact = 'Low')]
param
(
[Parameter(Mandatory,
ValueFromPipeline,
ValueFromPipelineByPropertyName,
Position = 0,
HelpMessage = 'Name of the Microsoft Teams team')]
[Parameter (Mandatory)]
[ValidateNotNullOrEmpty()]
[Alias('Name')]
[String]
$TeamName,
[Parameter(Mandatory,
ValueFromPipeline,
ValueFromPipelineByPropertyName,
Position = 1,
HelpMessage = 'Owner of the new Microsoft Teams team')]
[Parameter (Mandatory)]
[ValidateNotNullOrEmpty()]
[Alias('Owner')]
[String]
$TeamsOwner,
[Parameter(ValueFromPipeline,
ValueFromPipelineByPropertyName,
Position = 2)]
[Parameter (Mandatory)]
[ValidateNotNullOrEmpty()]
[String]
$privatepublic = 'Public',
[Parameter(ValueFromPipeline,
ValueFromPipelineByPropertyName,
Position = 3)]
[Parameter (Mandatory)]
[Alias('description')]
[String]
$TeamsDescription,
[Parameter(ValueFromPipeline,
ValueFromPipelineByPropertyName,
Position = 4)]
[Alias('OAuthClientId')]
[string]
$ClientId,
[Parameter(ValueFromPipeline,
ValueFromPipelineByPropertyName,
Position = 5)]
[Alias('OAuthTenantId')]
[string]
$TenantId,
[Parameter(Position = 6)]
[Alias('OAuthClientSecret')]
[string]
$ClientSecret,
[Parameter(ValueFromPipeline,
ValueFromPipelineByPropertyName,
Position = 7)]
[string]
$TenantName = 'contoso',
[Parameter(ValueFromPipeline,
ValueFromPipelineByPropertyName,
Position = 8)]
[string]
$DocumentLibrary = '/shared documents'
)
begin
{
<#
# Azure AD OAuth Application Token for Graph API
# Get OAuth token for a AAD Application (returned as $token)
# Application (client) ID, tenant ID and secret
$ClientId = 'xxxxxxxxxxxxxxxxxxxxxxxx'
$TenantId = 'xxxxxxxxxxxxxxxxxxxxxxxx'
$ClientSecret = 'xxxxxxxxxxxxxxxxxxxxxxxx'
#>
}
process
{
# Get the credentials to use
$Cred = (Get-Credential)
# Connect to Exchange Online
$paramNewPSSession = @{
ConfigurationName = 'Microsoft.Exchange'
ConnectionUri = 'https://outlook.office365.com/powershell-liveid'
Credential = $Cred
Authentication = 'Basic'
AllowRedirection = $true
}
$Session = (New-PSSession @paramNewPSSession)
$paramImportPSSession = @{
Session = $Session
DisableNameChecking = $true
AllowClobber = $true
}
$null = (Import-PSSession @paramImportPSSession)
# Connect to Microsoft Teams
$null = (Connect-MicrosoftTeams -Credential $Cred)
# Contruct URI
$uri = 'https://login.microsoftonline.com/' + $TenantId + '/oauth2/v2.0/token'
# Construct the JSON Body
$body1 = @{
client_id = $ClientId
scope = 'https://graph.microsoft.com/.default'
client_secret = $ClientSecret
grant_type = 'client_credentials'
}
try
{
# Get OAuth 2.0 Token
$paramInvokeWebRequest = @{
Method = 'Post'
Uri = $uri
ContentType = 'application/x-www-form-urlencoded'
Body = $body1
ErrorAction = 'Stop'
UseBasicParsing = $true
}
$tokenRequest = (Invoke-WebRequest @paramInvokeWebRequest)
}
catch
{
# get error record
[Management.Automation.ErrorRecord]$e = $_
# retrieve information about runtime error
$info = [PSCustomObject]@{
Exception = $e.Exception.Message
Reason = $e.CategoryInfo.Reason
Target = $e.CategoryInfo.TargetName
Script = $e.InvocationInfo.ScriptName
Line = $e.InvocationInfo.ScriptLineNumber
Column = $e.InvocationInfo.OffsetInLine
}
# output information. Post-process collected info, and log info (optional)
$info | Out-String | Write-Verbose
$paramWriteError = @{
Message = $e.Exception.Message
ErrorAction = 'Stop'
Exception = $e.Exception
TargetObject = $e.CategoryInfo.TargetName
}
Write-Error @paramWriteError
}
# Extract the Token
$token = (($tokenRequest.Content | ConvertFrom-Json).access_token)
# Get ID of team requester and set as owner.
$uri = 'https://graph.microsoft.com/beta/users/' + $TeamsOwner + '?$select=id'
$method = 'GET'
try
{
$paramInvokeWebRequest = @{
Method = $method
Uri = $uri
ContentType = 'application/json'
Headers = @{
Authorization = 'Bearer ' + $token
}
ErrorAction = 'Stop'
UseBasicParsing = $true
}
$query = (Invoke-WebRequest @paramInvokeWebRequest)
}
catch
{
# get error record
[Management.Automation.ErrorRecord]$e = $_
# retrieve information about runtime error
$info = [PSCustomObject]@{
Exception = $e.Exception.Message
Reason = $e.CategoryInfo.Reason
Target = $e.CategoryInfo.TargetName
Script = $e.InvocationInfo.ScriptName
Line = $e.InvocationInfo.ScriptLineNumber
Column = $e.InvocationInfo.OffsetInLine
}
# output information. Post-process collected info, and log info (optional)
$info | Out-String | Write-Verbose
$paramWriteError = @{
Message = $e.Exception.Message
ErrorAction = 'Stop'
Exception = $e.Exception
TargetObject = $e.CategoryInfo.TargetName
}
Write-Error @paramWriteError
}
# Extract the ID
$ownerID = (($query.content | ConvertFrom-Json).id)
# Specify the URI to call and method
$uri = 'https://graph.microsoft.com/beta/teams'
$method = 'Post'
# Construct the JSON Body
<#
Please review this defaults,
these settings are applied to the new Microsoft Teams team!
#>
$body = @"
{
"template@odata.bind": "https://graph.microsoft.com/beta/teamsTemplates/standard",
"displayName": "$TeamName",
"description": "$TeamsDescription",
"channels": [
{
"displayName": "01-Management",
"isFavoriteByDefault": true,
"description": "Description"
},
{
"displayName": "02-Developement",
"isFavoriteByDefault": true,
"description": "DEscription"
},
{
"displayName": "03-Marketing",
"isFavoriteByDefault": true,
"description": "Description"
},
{
"displayName": "04-Finance",
"isFavoriteByDefault": true,
"description": "Description"
}
],
"memberSettings": {
"allowCreateUpdateChannels": true,
"allowDeleteChannels": false,
"allowAddRemoveApps": true,
"allowCreateUpdateRemoveTabs": true,
"allowCreateUpdateRemoveConnectors": true
},
"guestSettings": {
"allowCreateUpdateChannels": false,
"allowDeleteChannels": false
},
"funSettings": {
"allowGiphy": true,
"giphyContentRating": "Moderate",
"allowStickersAndMemes": true,
"allowCustomMemes": true
},
"messagingSettings": {
"allowUserEditMessages": true,
"allowUserDeleteMessages": true,
"allowOwnerDeleteMessages": true,
"allowTeamMentions": true,
"allowChannelMentions": true
},
"visibility": "$Private",
"owners@odata.bind": [
"https://graph.microsoft.com/beta/users('$ownerID')"
]
}
"@
try
{
$paramInvokeWebRequest = @{
Method = $method
Uri = $uri
ContentType = 'application/json'
Body = $body
Headers = @{
Authorization = 'Bearer ' + $token
}
ErrorAction = 'Stop'
UseBasicParsing = $true
}
$query = (Invoke-WebRequest @paramInvokeWebRequest)
}
catch
{
# get error record
[Management.Automation.ErrorRecord]$e = $_
# retrieve information about runtime error
$info = [PSCustomObject]@{
Exception = $e.Exception.Message
Reason = $e.CategoryInfo.Reason
Target = $e.CategoryInfo.TargetName
Script = $e.InvocationInfo.ScriptName
Line = $e.InvocationInfo.ScriptLineNumber
Column = $e.InvocationInfo.OffsetInLine
}
# output information. Post-process collected info, and log info (optional)
$info | Out-String | Write-Verbose
$paramWriteError = @{
Message = $e.Exception.Message
ErrorAction = 'Stop'
Exception = $e.Exception
TargetObject = $e.CategoryInfo.TargetName
}
Write-Error @paramWriteError
}
# Extract the Data
$location = ($query.Headers).Location
$GroupID = $location.Substring(8, 36)
# Wait a minute to setup the stuff
Start-Sleep -Seconds 60
# Get the Mail info about the new team
$TeamSiteName = ((Get-Team -groupid $GroupID).MailNickName)
# Set some defaults
$SiteURL = 'https://' + $TenantName + '.sharepoint.com/sites/' + $TeamSiteName
$DocumentLibrary = '/shared documents'
# Channels
# Config Variables
$FolderNames = '01-Management', '02-Developement', '03-Marketing', '04-Finance'
# Relative URL of the Parent Folder
$RelativeURL = $DocumentLibrary
try
{
# Connect to PNP Online
$null = (Connect-PnPOnline -Url $SiteURL -Credentials $Cred)
#sharepoint online create folder powershell
foreach ($Folder in $FolderNames)
{
$paramAddPnPFolder = @{
Name = $Folder
Folder = $RelativeURL
ErrorAction = 'Stop'
}
$null = (Add-PnPFolder @paramAddPnPFolder)
Write-Verbose -Message ("New Folder '{0}' Added!" -f $Folder)
}
}
catch
{
# get error record
[Management.Automation.ErrorRecord]$e = $_
# retrieve information about runtime error
$info = [PSCustomObject]@{
Exception = $e.Exception.Message
Reason = $e.CategoryInfo.Reason
Target = $e.CategoryInfo.TargetName
Script = $e.InvocationInfo.ScriptName
Line = $e.InvocationInfo.ScriptLineNumber
Column = $e.InvocationInfo.OffsetInLine
}
# output information. Post-process collected info, and log info (optional)
$info | Out-String | Write-Verbose
$paramWriteError = @{
Message = $e.Exception.Message
ErrorAction = 'Continue'
Exception = $e.Exception
TargetObject = $e.CategoryInfo.TargetName
}
Write-Error @paramWriteError
}
# 01-Management
# Config Variables
$FolderNames = 'Meetings', 'Presentations'
$RelativeURL = $DocumentLibrary + '/01-management' #Relative URL of the Parent Folder
try
{
# Connect to PNP Online
$null = (Connect-PnPOnline -Url $SiteURL -Credentials $Cred)
# sharepoint online create folder powershell
foreach ($Folder in $FolderNames)
{
$paramAddPnPFolder = @{
Name = $Folder
Folder = $RelativeURL
ErrorAction = 'Stop'
}
$null = (Add-PnPFolder @paramAddPnPFolder)
Write-Verbose -Message ("New Folder '{0}' Added!" -f $Folder)
}
}
catch
{
# get error record
[Management.Automation.ErrorRecord]$e = $_
# retrieve information about runtime error
$info = [PSCustomObject]@{
Exception = $e.Exception.Message
Reason = $e.CategoryInfo.Reason
Target = $e.CategoryInfo.TargetName
Script = $e.InvocationInfo.ScriptName
Line = $e.InvocationInfo.ScriptLineNumber
Column = $e.InvocationInfo.OffsetInLine
}
# output information. Post-process collected info, and log info (optional)
$info | Out-String | Write-Verbose
$paramWriteError = @{
Message = $e.Exception.Message
ErrorAction = 'Continue'
Exception = $e.Exception
TargetObject = $e.CategoryInfo.TargetName
}
Write-Error @paramWriteError
}
# 02-Developement
# Config Variables
$FolderNames = 'Design', 'Specs', 'Labeling'
# Relative URL of the Parent Folder
$RelativeURL = $DocumentLibrary + '/02-Developement'
try
{
# Connect to PNP Online
$null = (Connect-PnPOnline -Url $SiteURL -Credentials $Cred)
# sharepoint online create folder powershell
foreach ($Folder in $FolderNames)
{
$paramAddPnPFolder = @{
Name = $Folder
Folder = $RelativeURL
ErrorAction = 'Stop'
}
$null = (Add-PnPFolder @paramAddPnPFolder)
Write-Verbose -Message ("New Folder '{0}' Added!" -f $Folder)
}
}
catch
{
# get error record
[Management.Automation.ErrorRecord]$e = $_
# retrieve information about runtime error
$info = [PSCustomObject]@{
Exception = $e.Exception.Message
Reason = $e.CategoryInfo.Reason
Target = $e.CategoryInfo.TargetName
Script = $e.InvocationInfo.ScriptName
Line = $e.InvocationInfo.ScriptLineNumber
Column = $e.InvocationInfo.OffsetInLine
}
# output information. Post-process collected info, and log info (optional)
$info | Out-String | Write-Verbose
$paramWriteError = @{
Message = $e.Exception.Message
ErrorAction = 'Continue'
Exception = $e.Exception
TargetObject = $e.CategoryInfo.TargetName
}
Write-Error @paramWriteError
}
# Subfolder
$FolderNames = 'Sketches', 'Requirements'
# Relative URL of the Parent Folder
$RelativeURL = $DocumentLibrary + '/02-Developement/Design'
try
{
# Connect to PNP Online
$null = (Connect-PnPOnline -Url $SiteURL -Credentials $Cred)
# sharepoint online create folder powershell
foreach ($Folder in $FolderNames)
{
$paramAddPnPFolder = @{
Name = $Folder
Folder = $RelativeURL
ErrorAction = 'Stop'
}
$null = (Add-PnPFolder @paramAddPnPFolder)
Write-Verbose -Message ("New Folder '{0}' Added!" -f $Folder)
}
}
catch
{
# get error record
[Management.Automation.ErrorRecord]$e = $_
# retrieve information about runtime error
$info = [PSCustomObject]@{
Exception = $e.Exception.Message
Reason = $e.CategoryInfo.Reason
Target = $e.CategoryInfo.TargetName
Script = $e.InvocationInfo.ScriptName
Line = $e.InvocationInfo.ScriptLineNumber
Column = $e.InvocationInfo.OffsetInLine
}
# output information. Post-process collected info, and log info (optional)
$info | Out-String | Write-Verbose
$paramWriteError = @{
Message = $e.Exception.Message
ErrorAction = 'Continue'
Exception = $e.Exception
TargetObject = $e.CategoryInfo.TargetName
}
Write-Error @paramWriteError
}
# 03-Marketing
# Config Variables
$FolderNames = 'Communication Brief', 'Competitor Review', 'Consumer Insights', 'Product FAQ', 'Product Information', 'Product Strategy'
# Relative URL of the Parent Folder
$RelativeURL = $DocumentLibrary + '/03-Marketing'
try
{
# Connect to PNP Online
$null = (Connect-PnPOnline -Url $SiteURL -Credentials $Cred)
# sharepoint online create folder powershell
foreach ($Folder in $FolderNames)
{
$paramAddPnPFolder = @{
Name = $Folder
Folder = $RelativeURL
ErrorAction = 'Stop'
}
$null = (Add-PnPFolder @paramAddPnPFolder)
Write-Verbose -Message ("New Folder '{0}' Added!" -f $Folder)
}
}
catch
{
# get error record
[Management.Automation.ErrorRecord]$e = $_
# retrieve information about runtime error
$info = [PSCustomObject]@{
Exception = $e.Exception.Message
Reason = $e.CategoryInfo.Reason
Target = $e.CategoryInfo.TargetName
Script = $e.InvocationInfo.ScriptName
Line = $e.InvocationInfo.ScriptLineNumber
Column = $e.InvocationInfo.OffsetInLine
}
# output information. Post-process collected info, and log info (optional)
$info | Out-String | Write-Verbose
$paramWriteError = @{
Message = $e.Exception.Message
ErrorAction = 'Continue'
Exception = $e.Exception
TargetObject = $e.CategoryInfo.TargetName
}
Write-Error @paramWriteError
}
# 04-Finance
# Config Variables
$FolderNames = 'Budget', 'Presentations'
# Relative URL of the Parent Folder
$RelativeURL = $DocumentLibrary + '/04-Finance'
try
{
# Connect to PNP Online
$null = (Connect-PnPOnline -Url $SiteURL -Credentials $Cred)
# sharepoint online create folder powershell
foreach ($Folder in $FolderNames)
{
$paramAddPnPFolder = @{
Name = $Folder
Folder = $RelativeURL
ErrorAction = 'Stop'
}
$null = (Add-PnPFolder @paramAddPnPFolder)
Write-Verbose -Message ("New Folder '{0}' Added!" -f $Folder)
}
}
catch
{
# get error record
[Management.Automation.ErrorRecord]$e = $_
# retrieve information about runtime error
$info = [PSCustomObject]@{
Exception = $e.Exception.Message
Reason = $e.CategoryInfo.Reason
Target = $e.CategoryInfo.TargetName
Script = $e.InvocationInfo.ScriptName
Line = $e.InvocationInfo.ScriptLineNumber
Column = $e.InvocationInfo.OffsetInLine
}
# output information. Post-process collected info, and log info (optional)
$info | Out-String | Write-Verbose
$paramWriteError = @{
Message = $e.Exception.Message
ErrorAction = 'Continue'
Exception = $e.Exception
TargetObject = $e.CategoryInfo.TargetName
}
Write-Error @paramWriteError
}
}
#region LICENSE
<#
BSD 3-Clause License
Copyright (c) 2021, enabling Technology
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#>
#endregion LICENSE
#region DISCLAIMER
<#
DISCLAIMER:
- Use at your own risk, etc.
- This is open-source software, if you find an issue try to fix it yourself. There is no support and/or warranty in any kind
- This is a third-party Software
- The developer of this Software is NOT sponsored by or affiliated with Microsoft Corp (MSFT) or any of its subsidiaries in any way
- The Software is not supported by Microsoft Corp (MSFT)
- By using the Software, you agree to the License, Terms, and any Conditions declared and described above
- If you disagree with any of the terms, and any conditions declared: Just delete it and build your own solution
#>
#endregion DISCLAIMER

View File

@@ -0,0 +1,300 @@
<#
.SYNOPSIS
Remove the Wiki tab on Microsoft Teams teams
.DESCRIPTION
Remove the Wiki tab on Microsoft Teams teams
I like Teams, but I never use the Wiki within Teams.
Alexander Holmeset figured out a smart way to get rid of the Wiki tab.
.PARAMETER ClientId
Azure AD Application (client) ID
.PARAMETER TenantId
Azure AD Tenant ID
.PARAMETER ClientSecret
Azure AD secret
.EXAMPLE
PS C:\> .\RemoveWiki.ps1 -ClientId 'Value1' -TenantId 'Value2' -ClientSecret 'Value3'
.NOTES
Original found in Alexander Holmeset's Blog
My version starts to make it a bit more flexible (e.g. more parameters)
.LINK
https://alexholmeset.blog/2019/05/10/remove-the-wiki-tab/
.LINK
https://gist.github.com/AlexanderHolmeset/e40c7e9297ae9cc01cb832871a9ff770
#>
[CmdletBinding(ConfirmImpact = 'Low')]
param
(
[Parameter(Mandatory,
ValueFromPipeline,
ValueFromPipelineByPropertyName,
Position = 0,
HelpMessage = 'Azure AD Application (client) ID')]
[ValidateNotNullOrEmpty()]
[Alias('OAuthClientId')]
[string]
$ClientId,
[Parameter(Mandatory,
ValueFromPipeline,
ValueFromPipelineByPropertyName,
Position = 1,
HelpMessage = 'Azure AD Tenant ID')]
[ValidateNotNullOrEmpty()]
[Alias('OAuthTenantId')]
[string]
$TenantId,
[Parameter(Mandatory,
ValueFromPipeline,
ValueFromPipelineByPropertyName,
Position = 2,
HelpMessage = 'Azure AD secret')]
[ValidateNotNullOrEmpty()]
[Alias('OAuthClientSecret')]
[string]
$ClientSecret
)
process
{
# Contruct URI
$uri = 'https://login.microsoftonline.com/' + $TenantId + '/oauth2/v2.0/token'
try
{
# Construct Body
$body1 = @{
client_id = $ClientId
scope = 'https://graph.microsoft.com/.default'
client_secret = $ClientSecret
grant_type = 'client_credentials'
}
# Get OAuth 2.0 Token
$paramInvokeWebRequest = @{
Method = 'Post'
Uri = $uri
ContentType = 'application/x-www-form-urlencoded'
Body = $body1
ErrorAction = 'Stop'
UseBasicParsing = $true
}
$tokenRequest = (Invoke-WebRequest @paramInvokeWebRequest)
}
catch
{
# get error record
[Management.Automation.ErrorRecord]$e = $_
# retrieve information about runtime error
$info = [PSCustomObject]@{
Exception = $e.Exception.Message
Reason = $e.CategoryInfo.Reason
Target = $e.CategoryInfo.TargetName
Script = $e.InvocationInfo.ScriptName
Line = $e.InvocationInfo.ScriptLineNumber
Column = $e.InvocationInfo.OffsetInLine
}
# output information. Post-process collected info, and log info (optional)
$info | Out-String | Write-Verbose
$paramWriteError = @{
Message = $e.Exception.Message
ErrorAction = 'Stop'
Exception = $e.Exception
TargetObject = $e.CategoryInfo.TargetName
}
Write-Error @paramWriteError
}
# Extract the Token
$token = ($tokenRequest.Content | ConvertFrom-Json).access_token
# Just in case
Write-Verbose -Message $token
try
{
# URI to call
$uri = 'https://graph.microsoft.com/v1.0/groups'
$paramInvokeRestMethod = @{
Method = 'GET'
Uri = $uri
ContentType = 'application/json'
Headers = @{
Authorization = 'Bearer ' + $token
}
ErrorAction = 'Stop'
}
$query = (Invoke-RestMethod @paramInvokeRestMethod)
}
catch
{
# get error record
[Management.Automation.ErrorRecord]$e = $_
# retrieve information about runtime error
$info = [PSCustomObject]@{
Exception = $e.Exception.Message
Reason = $e.CategoryInfo.Reason
Target = $e.CategoryInfo.TargetName
Script = $e.InvocationInfo.ScriptName
Line = $e.InvocationInfo.ScriptLineNumber
Column = $e.InvocationInfo.OffsetInLine
}
# output information. Post-process collected info, and log info (optional)
$info | Out-String | Write-Verbose
$paramWriteError = @{
Message = $e.Exception.Message
ErrorAction = 'Stop'
Exception = $e.Exception
TargetObject = $e.CategoryInfo.TargetName
}
Write-Error @paramWriteError
}
# Extract the Value
$groups = $query.value
foreach ($group in $groups)
{
try
{
if ($group.resourceProvisioningOptions -contains 'Team')
{
# Extract the ID
$id = $group.id
# Build the URI
$uri2 = 'https://graph.microsoft.com/v1.0/teams/' + $id + '/channels'
$paramInvokeRestMethod = @{
Method = 'Get'
Uri = $uri2
ContentType = 'application/json'
Headers = @{
Authorization = 'Bearer ' + $token
}
}
$query2 = (Invoke-RestMethod @paramInvokeRestMethod)
# Extract the Value
$Channels = $query2.value
foreach ($Channel in $Channels)
{
$id2 = $Channel.id
$uri3 = 'https://graph.microsoft.com/v1.0/teams/' + $id + '/channels/' + $id2 + '/tabs'
$paramInvokeRestMethod = @{
Method = 'Get'
Uri = $uri3
ContentType = 'application/json'
Headers = @{
Authorization = 'Bearer ' + $token
}
}
$query3 = (Invoke-RestMethod @paramInvokeRestMethod)
# Extract the Value
$tabs = $query3.value
# Find the Wiki Tab
$WikiTabs = ($tabs | Where-Object -FilterScript {
$_.displayname -eq 'Wiki'
})
if ($WikiTabs)
{
foreach ($wikitab in $WikiTabs)
{
# Extract the ID
$wikitabID = $wikitab.id
# Build the URI
$uri4 = 'https://graph.microsoft.com/v1.0/teams/' + $id + '/channels/' + $id2 + '/tabs/' + $wikitabID
$paramInvokeRestMethod = @{
Method = 'DELETE'
Uri = $uri4
ContentType = 'application/json'
Headers = @{
Authorization = 'Bearer ' + $token
}
}
$query4 = (Invoke-RestMethod @paramInvokeRestMethod)
Write-Verbose -Message $query4
Write-Output -InputObject 'wikitab removed'
}
}
}
}
}
catch
{
# get error record
[Management.Automation.ErrorRecord]$e = $_
# retrieve information about runtime error
$info = [PSCustomObject]@{
Exception = $e.Exception.Message
Reason = $e.CategoryInfo.Reason
Target = $e.CategoryInfo.TargetName
Script = $e.InvocationInfo.ScriptName
Line = $e.InvocationInfo.ScriptLineNumber
Column = $e.InvocationInfo.OffsetInLine
}
# output information. Post-process collected info, and log info (optional)
$info | Out-String | Write-Verbose
$paramWriteError = @{
Message = $e.Exception.Message
ErrorAction = 'Continue'
Exception = $e.Exception
TargetObject = $e.CategoryInfo.TargetName
}
Write-Error @paramWriteError
}
}
}
#region LICENSE
<#
BSD 3-Clause License
Copyright (c) 2021, enabling Technology
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#>
#endregion LICENSE
#region DISCLAIMER
<#
DISCLAIMER:
- Use at your own risk, etc.
- This is open-source software, if you find an issue try to fix it yourself. There is no support and/or warranty in any kind
- This is a third-party Software
- The developer of this Software is NOT sponsored by or affiliated with Microsoft Corp (MSFT) or any of its subsidiaries in any way
- The Software is not supported by Microsoft Corp (MSFT)
- By using the Software, you agree to the License, Terms, and any Conditions declared and described above
- If you disagree with any of the terms, and any conditions declared: Just delete it and build your own solution
#>
#endregion DISCLAIMER

View File

@@ -0,0 +1,131 @@
<#
.SYNOPSIS
Replace the Domain for all UnifiedGroups (and Microsoft Teams) Primary SMTP Address
.DESCRIPTION
Replace the Domain for all UnifiedGroups (and Microsoft Teams) Primary SMTP Address
.PARAMETER OldDomain
The old Domain (e.g. contoso.com)
.PARAMETER NewDomain
The new Domain (e.g. contoso.net)
.EXAMPLE
PS C:\> .\ReplaceDomainForAllUnifiedGroups.ps1 -OldDomain 'contoso.com' -NewDomain 'contoso.net'
Replace the Primary SMTP Addresses for all UnifiedGroups (and Microsoft Teams) that are in the domain 'contoso.com' with the someone in the Domain 'contoso.net'
e.g. if an old address was myTeam@contoso.com would end up as myTeam@contoso.new
.LINK
https://docs.microsoft.com/en-us/powershell/exchange/exchange-online/connect-to-exchange-online-powershell/connect-to-exchange-online-powershell?view=exchange-ps
.LINK
http://hochwald.net
.NOTES
Quick and dirty approach, without any real Error handling.
A friend asked me for a solution after a merger to replace all Primary SMTP Addresses and get rid of the old domain (legal requirement in this case)
You need be be connected to an Exchange Online Session (NOT part of this script).
#>
[CmdletBinding(ConfirmImpact = 'Low',
SupportsShouldProcess = $true)]
param
(
[Parameter(Mandatory = $true,
ValueFromPipeline = $true,
ValueFromPipelineByPropertyName = $true)]
[ValidateNotNullOrEmpty()]
[Alias('DomainToReplace')]
[string]
$OldDomain,
[Parameter(Mandatory = $true,
ValueFromPipeline = $true,
ValueFromPipelineByPropertyName = $true)]
[ValidateNotNullOrEmpty()]
[string]
$NewDomain
)
begin
{
$OldMailFilter = ('@' + $OldDomain)
# Cleanup
$AllUnifiedGroups = $null
}
process
{
$AllUnifiedGroups = (Get-UnifiedGroup | Where-Object -FilterScript {
$_.PrimarySmtpAddress -like ('*' + $OldMailFilter)
} | Select-Object -Property Identity, DisplayName, PrimarySmtpAddress)
if ($AllUnifiedGroups)
{
foreach ($item in $AllUnifiedGroups)
{
if ($item.PrimarySmtpAddress -like ('*' + $OldMailFilter))
{
$OldMailAddress = $null
$OldMailAddress = (($item).PrimarySmtpAddress)
$NewMailAddress = $null
$NewMailAddress = ($OldMailAddress.Replace($OldMailFilter, ('@' + $NewDomain)))
Write-Verbose -Message ('Replace: {0} with: {1}' -f $OldMailAddress, $NewMailAddress)
# Add the new Address
$null = (Set-UnifiedGroup -Identity (($item).Identity) -EmailAddresses: @{
Add = $NewMailAddress
} -Confirm:$false)
# Make new Address the primary SMTP address
$null = (Set-UnifiedGroup -Identity (($item).Identity) -PrimarySmtpAddress $NewMailAddress -Confirm:$false)
# Remove the old SMTP Address
$null = (Set-UnifiedGroup -Identity (($item).Identity) -EmailAddresses: @{
Remove = $OldMailAddress
} -Confirm:$false)
}
else
{
Write-Warning -Message ('Sorry, the PrimarySmtpAddress of {0} is not in {1}' -f $item.DisplayName, $OldDomain)
}
}
}
else
{
Write-Output -InputObject 'Nothing to do!!!'
}
}
#region LICENSE
<#
BSD 3-Clause License
Copyright (c) 2021, enabling Technology
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#>
#endregion LICENSE
#region DISCLAIMER
<#
DISCLAIMER:
- Use at your own risk, etc.
- This is open-source software, if you find an issue try to fix it yourself. There is no support and/or warranty in any kind
- This is a third-party Software
- The developer of this Software is NOT sponsored by or affiliated with Microsoft Corp (MSFT) or any of its subsidiaries in any way
- The Software is not supported by Microsoft Corp (MSFT)
- By using the Software, you agree to the License, Terms, and any Conditions declared and described above
- If you disagree with any of the terms, and any conditions declared: Just delete it and build your own solution
#>
#endregion DISCLAIMER

View File

@@ -0,0 +1,210 @@
#requires -Version 3.0 -Modules NetQos -RunAsAdministrator
<#
.SYNOPSIS
Apply QoS Settings for Microsoft Teams
.DESCRIPTION
Apply Network Quality of Service (QoS) settings for Microsoft Teams.
.PARAMETER AppPathNameMatchCondition
Specifies the name by which an application is run, such as application.exe or %ProgramFiles%\application.exe application.
.EXAMPLE
PS C:\> .\Set-QoSForMicrosoftTeams.ps1
.EXAMPLE
PS C:\> .\Set-QoSForMicrosoftTeamsRoom.ps1 -AppPathNameMatchCondition 'Teams.exe'
.NOTES
Changelog:
1.0.0: Initial Release (Adopted from Set-QoSForMicrosoftTeamsRoomDevices.ps1)
Version 1.0.0
.LINK
Get-NetQosPolicy
.LINK
New-NetQosPolicy
.LINK
https://docs.microsoft.com/en-us/microsoftteams/qos-in-teams-clients
.LINK
http://enatec.io
#>
[CmdletBinding(ConfirmImpact = 'Low',
SupportsShouldProcess)]
param
(
[Parameter(ValueFromPipeline,
ValueFromPipelineByPropertyName)]
[Alias('AppName')]
[string]
$AppPathNameMatchCondition = 'Teams.exe'
)
begin
{
Write-Output -InputObject 'Apply Network Quality of Service (QoS) settings for Microsoft Teams'
#region Defaults
$CNT = 'Continue'
$STP = 'Stop'
$SCT = 'SilentlyContinue'
[string]$AppSharingPolicy = 'Microsoft Teams AppSharing'
[string]$VideoPolicy = 'Microsoft Teams Video'
[string]$AudioPoliy = 'Microsoft Teams Audio'
#endregion Defaults
}
process
{
if ($pscmdlet.ShouldProcess('QoS-Settings', 'Apply'))
{
#region Audio
$paramGetNetQosPolicy = @{
Name = $AudioPoliy
ErrorAction = $SCT
WarningAction = $SCT
}
if (-not (Get-NetQosPolicy @paramGetNetQosPolicy))
{
try
{
# Splat the parameters
$paramNewNetQosPolicy = @{
NetworkProfile = 'All'
IPSrcPortStartMatchCondition = 50000
IPSrcPortEndMatchCondition = 50019
DSCPAction = 46
IPProtocolMatchCondition = 'Both'
Name = $AudioPoliy
Confirm = $false
WarningAction = $CNT
ErrorAction = $STP
}
# Do we have an application name?
if ($AppPathNameMatchCondition)
{
$paramNewNetQosPolicy.Add('AppPathNameMatchCondition', $AppPathNameMatchCondition)
}
$null = (New-NetQosPolicy @paramNewNetQosPolicy)
}
catch
{
Write-Warning -Message ('Unable to apply {0} QoS Poliy' -f $AudioPoliy)
}
}
#endregion Audio
#region Video
$paramGetNetQosPolicy = @{
Name = $VideoPolicy
ErrorAction = $SCT
WarningAction = $SCT
}
if (-not (Get-NetQosPolicy @paramGetNetQosPolicy))
{
try
{
# Splat the parameters
$paramNewNetQosPolicy = @{
NetworkProfile = 'All'
IPSrcPortStartMatchCondition = 50020
IPSrcPortEndMatchCondition = 50039
DSCPAction = 34
IPProtocolMatchCondition = 'Both'
Name = $VideoPolicy
Confirm = $false
WarningAction = $CNT
ErrorAction = $STP
}
# Do we have an application name?
if ($AppPathNameMatchCondition)
{
$paramNewNetQosPolicy.Add('AppPathNameMatchCondition', $AppPathNameMatchCondition)
}
$null = (New-NetQosPolicy @paramNewNetQosPolicy)
}
catch
{
Write-Warning -Message ('Unable to apply {0} QoS Poliy' -f $VideoPolicy)
}
}
#endregion Video
#region AppSharing
$paramGetNetQosPolicy = @{
Name = $AppSharingPolicy
ErrorAction = $SCT
WarningAction = $SCT
}
if (-not (Get-NetQosPolicy @paramGetNetQosPolicy))
{
try
{
# Splat the parameters
$paramNewNetQosPolicy = @{
NetworkProfile = 'All'
IPSrcPortStartMatchCondition = 50040
IPSrcPortEndMatchCondition = 50059
DSCPAction = 28
IPProtocolMatchCondition = 'Both'
Name = $AppSharingPolicy
Confirm = $false
WarningAction = $CNT
ErrorAction = $STP
}
# Do we have an application name?
if ($AppPathNameMatchCondition)
{
$paramNewNetQosPolicy.Add('AppPathNameMatchCondition', $AppPathNameMatchCondition)
}
$null = (New-NetQosPolicy @paramNewNetQosPolicy)
}
catch
{
Write-Warning -Message ('Unable to apply {0} QoS Poliy' -f $AppSharingPolicy)
}
}
#endregion AppSharing
}
}
#region LICENSE
<#
BSD 3-Clause License
Copyright (c) 2021, enabling Technology
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#>
#endregion LICENSE
#region DISCLAIMER
<#
DISCLAIMER:
- Use at your own risk, etc.
- This is open-source software, if you find an issue try to fix it yourself. There is no support and/or warranty in any kind
- This is a third-party Software
- The developer of this Software is NOT sponsored by or affiliated with Microsoft Corp (MSFT) or any of its subsidiaries in any way
- The Software is not supported by Microsoft Corp (MSFT)
- By using the Software, you agree to the License, Terms, and any Conditions declared and described above
- If you disagree with any of the terms, and any conditions declared: Just delete it and build your own solution
#>
#endregion DISCLAIMER

View File

@@ -0,0 +1,203 @@
#requires -Version 3.0 -Modules NetQos -RunAsAdministrator
<#
.SYNOPSIS
Apply QoS Settings for Microsoft Teams Room Devices
.DESCRIPTION
Apply Network Quality of Service (QoS) settings for Microsoft Teams Room Devices.
I use this script to deploy the QoS settings to MTR devices via Intune.
.PARAMETER AppPathNameMatchCondition
Specifies the name by which an application is run, such as application.exe or %ProgramFiles%\application.exe application.
.EXAMPLE
PS C:\> .\Set-QoSForMicrosoftTeamsRoomDevices.ps1
.EXAMPLE
PS C:\> .\Set-QoSForMicrosoftTeamsRoomDevices.ps1 -AppPathNameMatchCondition 'Teams.exe'
.NOTES
Idea based on a Twitter chat with @StaleHansen
Please ensure to check the Ports!
They must match you Teams Admin Centr (TAC) settings.
.LINK
Get-NetQosPolicy
.LINK
New-NetQosPolicy
.LINK
https://docs.microsoft.com/en-us/microsoftteams/qos-in-teams-clients
.LINK
https://twitter.com/StaleHansen/status/1294341225647083522
#>
[CmdletBinding(ConfirmImpact = 'Low',
SupportsShouldProcess)]
param
(
[Parameter(ValueFromPipeline,
ValueFromPipelineByPropertyName)]
[Alias('AppName')]
[string]
$AppPathNameMatchCondition = $null
)
begin
{
$AppSharingPolicy = 'MTR AppSharing'
$VideoPolicy = 'MTR Video'
$AudioPoliy = 'MTR Audio'
}
process
{
if ($pscmdlet.ShouldProcess('QoS-Settings', 'Apply'))
{
#region Audio
$paramGetNetQosPolicy = @{
Name = $AudioPoliy
ErrorAction = $SCT
WarningAction = $SCT
}
if (-not (Get-NetQosPolicy @paramGetNetQosPolicy))
{
try
{
# Splat the parameters
$paramNewNetQosPolicy = @{
NetworkProfile = 'All'
IPSrcPortStartMatchCondition = 50000
IPSrcPortEndMatchCondition = 50019
DSCPAction = 46
IPProtocolMatchCondition = 'Both'
Name = $AudioPoliy
Confirm = $false
WarningAction = $CNT
ErrorAction = $STP
}
# Do we have an application name?
if ($AppPathNameMatchCondition)
{
$paramNewNetQosPolicy.Add('AppPathNameMatchCondition', $AppPathNameMatchCondition)
}
$null = (New-NetQosPolicy @paramNewNetQosPolicy)
}
catch
{
Write-Warning -Message ('Unable to apply {0} QoS Poliy' -f $AudioPoliy)
}
}
#endregion Audio
#region Video
$paramGetNetQosPolicy = @{
Name = $VideoPolicy
ErrorAction = $SCT
WarningAction = $SCT
}
if (-not (Get-NetQosPolicy @paramGetNetQosPolicy))
{
try
{
# Splat the parameters
$paramNewNetQosPolicy = @{
NetworkProfile = 'All'
IPSrcPortStartMatchCondition = 50020
IPSrcPortEndMatchCondition = 50039
DSCPAction = 34
IPProtocolMatchCondition = 'Both'
Name = $VideoPolicy
Confirm = $false
WarningAction = $CNT
ErrorAction = $STP
}
# Do we have an application name?
if ($AppPathNameMatchCondition)
{
$paramNewNetQosPolicy.Add('AppPathNameMatchCondition', $AppPathNameMatchCondition)
}
$null = (New-NetQosPolicy @paramNewNetQosPolicy)
}
catch
{
Write-Warning -Message ('Unable to apply {0} QoS Poliy' -f $VideoPolicy)
}
}
#endregion Video
#region AppSharing
$paramGetNetQosPolicy = @{
Name = $AppSharingPolicy
ErrorAction = $SCT
WarningAction = $SCT
}
if (-not (Get-NetQosPolicy @paramGetNetQosPolicy))
{
try
{
# Splat the parameters
$paramNewNetQosPolicy = @{
NetworkProfile = 'All'
IPSrcPortStartMatchCondition = 50040
IPSrcPortEndMatchCondition = 50059
DSCPAction = 28
IPProtocolMatchCondition = 'Both'
Name = $AppSharingPolicy
Confirm = $false
WarningAction = $CNT
ErrorAction = $STP
}
# Do we have an application name?
if ($AppPathNameMatchCondition)
{
$paramNewNetQosPolicy.Add('AppPathNameMatchCondition', $AppPathNameMatchCondition)
}
$null = (New-NetQosPolicy @paramNewNetQosPolicy)
}
catch
{
Write-Warning -Message ('Unable to apply {0} QoS Poliy' -f $AppSharingPolicy)
}
}
#endregion AppSharing
}
}
#region LICENSE
<#
BSD 3-Clause License
Copyright (c) 2021, enabling Technology
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#>
#endregion LICENSE
#region DISCLAIMER
<#
DISCLAIMER:
- Use at your own risk, etc.
- This is open-source software, if you find an issue try to fix it yourself. There is no support and/or warranty in any kind
- This is a third-party Software
- The developer of this Software is NOT sponsored by or affiliated with Microsoft Corp (MSFT) or any of its subsidiaries in any way
- The Software is not supported by Microsoft Corp (MSFT)
- By using the Software, you agree to the License, Terms, and any Conditions declared and described above
- If you disagree with any of the terms, and any conditions declared: Just delete it and build your own solution
#>
#endregion DISCLAIMER

View File

@@ -0,0 +1,128 @@
<#
.SYNOPSIS
Create a Microsoft Teams Room Device in Office 365
.DESCRIPTION
Create and setup a Microsoft Teams Room Device in Microsoft Office 365
.NOTES
Review the variable here.
You must be connected to the following Services:
- Exchange Online
- MSOL (Not AzureAD!)
- Skype for Business Online (Not Teams!)
.LINK
https://hochwald.net/microsoft-teams-room-device-1-2/
.LINK
https://hochwald.net/microsoft-teams-room-device-2-2/
#>
[CmdletBinding(ConfirmImpact = 'Low')]
param ()
# Display Name of the Room
$RoomName = 'Your-Teams-Room'
# Alias of the Room (For the UPN, SMTP, and SIP Address)
$RoomAlias = 'YourTeamsRoom'
# Keep this safe
$RoomPassword = 'YourSuperSecretRoomPassword'
<#
At the moment, the password for a room/resource will never expire!
So keep this in a safe place. And there is no second factor (MFA).
#>
# The Domain for the UPN, and the SMTP address
$RoomDomain = 'contoso.com'
# The Response text for meeting requests
$RoomAdditionalResponse = 'This is a Microsoft Teams Team Room'
<#
Basic HTML is supported here!
This text will be in the meeting respoinse mail, so use it as a info or teaser
#>
# The ALIAS of the license to apply.
# In this case it is the MEETING_ROOM License in the tenant with the name contoso
$RoomLicence = 'contoso:MEETING_ROOM'
<#
The license must be availible (Buy it before create the room)
#>
# We need one User that we use to find the SIP Registrar Pool (For Skype for Business and Teams SIP handling)
$CsOnlineUser = 'john.doe'
#region AutomatedStrings
# Build some strings
$RoomUserPrincipalName = ($RoomAlias + '@' + $RoomDomain)
$CsOnlineUserTemplate = ($CsOnlineUser + '@' + $RoomDomain)
<#
I use the same domain for the UPN and the SMTP/SIP address,
and I highly recommend you to do the same!
#>
#endregion AutomatedStrings
#region NewMailbox
# Create the Mailbox
$paramNewMailbox = @{
Name = $RoomName
Alias = $RoomAlias
Room = $true
EnableRoomMailboxAccount = $true
MicrosoftOnlineServicesID = $RoomUserPrincipalName
RoomMailboxPassword = (ConvertTo-SecureString -String $RoomPassword -AsPlainText -Force)
}
New-Mailbox @paramNewMailbox
#endregion NewMailbox
#region SetCalendarProcessing
# Tweak Calendar settings
$paramSetCalendarProcessing = @{
Identity = $RoomName
AutomateProcessing = 'AutoAccept'
AddOrganizerToSubject = $false
DeleteComments = $false
DeleteSubject = $false
RemovePrivateProperty = $false
AddAdditionalResponse = $true
AdditionalResponse = $RoomAdditionalResponse
}
Set-CalendarProcessing @paramSetCalendarProcessing
<#
Please review the parameters above!
They might not match your taste or requirements
You can add more: Use 'Get-Help Set-CalendarProcessing -details' to see all supported paramaters
#>
#endregion SetCalendarProcessing
#region SetMsolUser
# Usage location and password tweak
$paramSetMsolUser = @{
UserPrincipalName = $RoomUserPrincipalName
PasswordNeverExpires = $true
UsageLocation = 'DE'
}
Set-MsolUser @paramSetMsolUser
#endregion SetMsolUser
#region SetMsolUserLicense
# Apply the license
$paramSetMsolUserLicense = @{
UserPrincipalName = $RoomUserPrincipalName
AddLicenses = $RoomLicence
}
Set-MsolUserLicense @paramSetMsolUserLicense
#endregion SetMsolUserLicense
#region EnableCsMeetingRoom
# Enable SIP (Skype for Business/Teams)
$paramEnableCsMeetingRoom = @{
Identity = $RoomUserPrincipalName
RegistrarPool = (Get-CsOnlineUser -Identity $CsOnlineUserTemplate | Select-Object -ExpandProperty RegistrarPool)
SipAddressType = 'EmailAddress'
}
Enable-CsMeetingRoom @paramEnableCsMeetingRoom
#endregion EnableCsMeetingRoom

View File

@@ -0,0 +1,227 @@
function Update-UnifiedGroupsToTeams
{
<#
.SYNOPSIS
Converts all Microsoft Office 365 Groups into a new Microsoft Teams Team
.DESCRIPTION
Converts all Microsoft Office 365 Groups into a new Microsoft Teams Team
Microsoft Office 365 Groups are also known as Unified Office 365 Groups
.PARAMETER ReportOnly
Shows a list of Microsoft Office 365 Groups that would be migrated to a new Microsoft Teams Team.
This is a DryRun only!
.EXAMPLE
PS C:\> Update-UnifiedGroupsToTeams
Converting all Microsoft Office 365 Groups into a new Microsoft Teams Team
.EXAMPLE
PS C:\> Update-UnifiedGroupsToTeams -ReportOnly
Do a DryRun (Just get a List of Unified Groups that do NOT have a Microsoft Teams Team)
.EXAMPLE
PS C:\> Compare-Object -ReferenceObject ((Get-Team | Select-Object -ExpandProperty GroupId)) -DifferenceObject ((Get-UnifiedGroup -ResultSize Unlimited | Select-Object -ExpandProperty ExternalDirectoryObjectId)) -PassThru
Get a short difference list (this function is not required to do so)
.NOTES
Releasenotes:
1.0.1 2019-04-21: Add a bit more error handling
1.0.0 2018-04-14: Internal Release
THIS CODE IS MADE AVAILABLE AS IS, WITHOUT WARRANTY OF ANY KIND. THE ENTIRE RISK OF THE USE OR THE RESULTS FROM THE USE OF THIS CODE REMAINS WITH THE USER.
Dependencies:
The script depends on Microsoft's Version 0.9.6, or newer, of the MicrosoftTeams PowerShell Module
Install it with PowerShellGet:
PS C:\> Install-Module MicrosoftTeams
You need to be connected to Office 365 (Exchange Online). The function will check that.
.LINK
https://www.powershellgallery.com/packages/MicrosoftTeams/0.9.6
.LINK
https://aka.ms/InstallModule
#>
[CmdletBinding(ConfirmImpact = 'Low')]
param
(
[Parameter(ValueFromPipeline,
ValueFromPipelineByPropertyName,
Position = 0)]
[Alias('DryRun')]
[switch]
$ReportOnly
)
begin
{
#region Defaults
$CNT = 'Continue'
$STP = 'Stop'
#endregion Defaults
try
{
#region ConnectionCheck
if (-not (Get-Command -Name Get-UnifiedGroup -ErrorAction SilentlyContinue))
{
$ErrorParameter = @{
Message = 'Please connect to Office 365/Exchange Online before using this function!'
Category = 'ResourceUnavailable'
RecommendedAction = 'Connect to Office 365/Exchange Online before using this function'
ErrorAction = $STP
}
Write-Error @ErrorParameter
}
#endregion ConnectionCheck
#region GetUnifiedGroups
$GetUnifiedGroupParameter = @{
ResultSize = 'Unlimited'
ErrorAction = $STP
WarningAction = $CNT
}
$AllOffice365UnifiedGroups = (Get-UnifiedGroup @GetUnifiedGroupParameter | Select-Object -Property DisplayName, ExternalDirectoryObjectId)
#endregion GetUnifiedGroups
#region GetMicrosoftTeams
$GetTeamParameter = @{
ErrorAction = $STP
WarningAction = $CNT
}
$AllMicrosoftTeams = (Get-Team @GetTeamParameter | Select-Object -ExpandProperty GroupId)
#endregion GetMicrosoftTeams
}
catch
{
# Get error record
[Management.Automation.ErrorRecord]$e = $_
# Retrieve information about runtime error
$info = [PSCustomObject]@{
Exception = $e.Exception.Message
Reason = $e.CategoryInfo.Reason
Target = $e.CategoryInfo.TargetName
Script = $e.InvocationInfo.ScriptName
Line = $e.InvocationInfo.ScriptLineNumber
Column = $e.InvocationInfo.OffsetInLine
}
# Dump the FULL error record
Write-Warning -Message ($info | Out-String)
Write-Error -Message $info.Exception -Exception $info.Exception -ErrorAction $STP
break
}
}
process
{
if ($AllOffice365UnifiedGroups)
{
#region Loop
foreach ($Office365UnifiedGroup in $AllOffice365UnifiedGroups)
{
if (-not ($AllMicrosoftTeams -match $Office365UnifiedGroup.ExternalDirectoryObjectId))
{
if ($ReportOnly)
{
#region ReportOnly
$SingleOffice365UnifiedGroup = $Office365UnifiedGroup.DisplayName
Write-Output -InputObject ('Microsoft Teams for Unified Group {0} is missing' -f $SingleOffice365UnifiedGroup)
#endregion ReportOnly
}
else
{
#region CreateMissingTeam
Write-Verbose -Message ('Create Microsoft Teams Team for Unified Group {0}' -f $SingleOffice365UnifiedGroup)
try
{
$NewTeamParameter = @{
Group = $Office365UnifiedGroup
ErrorAction = $STP
WarningAction = $CNT
}
$NewTeam = (New-Team @NewTeamParameter)
Write-Debug -Message $NewTeam
Write-Verbose -Message ('Created Microsoft Teams Team for Unified Group {0}' -f $SingleOffice365UnifiedGroup)
}
catch
{
# Get error record
[Management.Automation.ErrorRecord]$e = $_
# Retrieve information about runtime error
$info = [PSCustomObject]@{
Exception = $e.Exception.Message
Reason = $e.CategoryInfo.Reason
Target = $e.CategoryInfo.TargetName
Script = $e.InvocationInfo.ScriptName
Line = $e.InvocationInfo.ScriptLineNumber
Column = $e.InvocationInfo.OffsetInLine
}
$TheException = $info.Exception
Write-Warning -Message ('Microsoft Teams creation for {0} failed with {1} ' -f $SingleOffice365UnifiedGroup, $TheException)
# Dump the FULL error record
Write-Verbose -Message ($info | Out-String)
}
#endregion CreateMissingTeam
}
}
}
#endregion Loop
}
else
{
Write-Warning -Message 'No Unified Groups found in your Tenant...'
}
}
end
{
Write-Verbose -Message 'Done.'
}
}
#region LICENSE
<#
BSD 3-Clause License
Copyright (c) 2021, enabling Technology
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#>
#endregion LICENSE
#region DISCLAIMER
<#
DISCLAIMER:
- Use at your own risk, etc.
- This is open-source software, if you find an issue try to fix it yourself. There is no support and/or warranty in any kind
- This is a third-party Software
- The developer of this Software is NOT sponsored by or affiliated with Microsoft Corp (MSFT) or any of its subsidiaries in any way
- The Software is not supported by Microsoft Corp (MSFT)
- By using the Software, you agree to the License, Terms, and any Conditions declared and described above
- If you disagree with any of the terms, and any conditions declared: Just delete it and build your own solution
#>
#endregion DISCLAIMER

View File

@@ -0,0 +1,23 @@
# Use the latest Microsoft Teams PowerShell Module to connect
# Not the Skype for Business Online Module (outdated)
# Get all Teams Meeting Policies
Get-CsTeamsMeetingPolicy | Select-Object -ExpandProperty Identity
# Get all Teams Meeting Policies, exclude all TAG Policies (You can not modify them with Get-CsTeamsMeetingPolicy)
Get-CsTeamsMeetingPolicy | Where-Object -FilterScript {
$_.Identity -notlike 'Tag:*'
} | Select-Object -ExpandProperty Identity
# Modify the Global Policy
Set-CsTeamsMeetingPolicy -Identity Global -AllowEngagementReport Enabled
# Modify any Policy by name
Set-CsTeamsMeetingPolicy -Identity 'Meetings' | Set-CsTeamsMeetingPolicy -AllowEngagementReport Enabled
# Modify all Policies (exclude the TAG Policies, because you can not modify them with Get-CsTeamsMeetingPolicy)
Get-CsTeamsMeetingPolicy | Where-Object -FilterScript {
$_.Identity -notlike 'Tag:*'
} | ForEach-Object -Process {
Set-CsTeamsMeetingPolicy -Identity $_.Identity -AllowEngagementReport Enabled -ErrorAction Continue
}

View File

@@ -0,0 +1,16 @@
RuleID,RuleDescription,RuleAction
75668C1F-73B5-4CF0-BB93-3ECF5CB7CC84, Block Office applications from injecting into other processes, Enabled
3B576869-A4EC-4529-8536-B80A7769E899, Block Office applications from creating executable content, Enabled
D4F940AB-401B-4EfC-AADC-AD5F3C50688A, Block Office applications from creating child processes, Enabled
D3E037E1-3EB8-44C8-A917-57927947596D, Impede JavaScript and VBScript to launch executables, Enabled
5BEB7EFE-FD9A-4556-801D-275E5FFC04CC, Block execution of potentially obfuscated script, Enabled
BE9BA2D9-53EA-4CDC-84E5-9B1EEEE46550, Block executable content from email client and webmail, Enabled
92E97FA1-2EDF-4476-BDD6-9DD0B4DDDC7B, Block Win32 imports from Macro code in Office, Enabled
c1db55ab-c21a-4637-bb3f-a12568109d35, Use advanced protection against ransomware, Enabled
9e6c4e1f-7d60-472f-ba1a-a39ef669e4b2, Block credential stealing from the Windows local security authority subsystem (lsass.exe), Enabled
d1e49aac-8f56-4280-b9ba-993a6d77406c, Block process creations originating from PSExec and WMI commands, Enabled
b2b3f03d-6a65-4f7b-a9c7-1c7ef74a9ba4, Block untrusted and unsigned processes that run from USB, Enabled
26190899-1602-49e8-8b27-eb1d0a1ce869, Block Office communication applications from creating child processes, AuditMode
7674ba52-37eb-4a4f-a9a1-f0f9a1619a2c, Block Adobe Reader from creating child processes, Enabled
e6db77e5-3df2-4cf1-b95a-636979351e5b, Block persistence through WMI event subscription, Enabled
01443614-cd74-433a-b99e-2ecdc07bfc25, Block executable files from running unless they meet a prevalence age or trusted list criteria, AuditMode
1 RuleID RuleDescription RuleAction
2 75668C1F-73B5-4CF0-BB93-3ECF5CB7CC84 Block Office applications from injecting into other processes Enabled
3 3B576869-A4EC-4529-8536-B80A7769E899 Block Office applications from creating executable content Enabled
4 D4F940AB-401B-4EfC-AADC-AD5F3C50688A Block Office applications from creating child processes Enabled
5 D3E037E1-3EB8-44C8-A917-57927947596D Impede JavaScript and VBScript to launch executables Enabled
6 5BEB7EFE-FD9A-4556-801D-275E5FFC04CC Block execution of potentially obfuscated script Enabled
7 BE9BA2D9-53EA-4CDC-84E5-9B1EEEE46550 Block executable content from email client and webmail Enabled
8 92E97FA1-2EDF-4476-BDD6-9DD0B4DDDC7B Block Win32 imports from Macro code in Office Enabled
9 c1db55ab-c21a-4637-bb3f-a12568109d35 Use advanced protection against ransomware Enabled
10 9e6c4e1f-7d60-472f-ba1a-a39ef669e4b2 Block credential stealing from the Windows local security authority subsystem (lsass.exe) Enabled
11 d1e49aac-8f56-4280-b9ba-993a6d77406c Block process creations originating from PSExec and WMI commands Enabled
12 b2b3f03d-6a65-4f7b-a9c7-1c7ef74a9ba4 Block untrusted and unsigned processes that run from USB Enabled
13 26190899-1602-49e8-8b27-eb1d0a1ce869 Block Office communication applications from creating child processes AuditMode
14 7674ba52-37eb-4a4f-a9a1-f0f9a1619a2c Block Adobe Reader from creating child processes Enabled
15 e6db77e5-3df2-4cf1-b95a-636979351e5b Block persistence through WMI event subscription Enabled
16 01443614-cd74-433a-b99e-2ecdc07bfc25 Block executable files from running unless they meet a prevalence age or trusted list criteria AuditMode

View File

@@ -0,0 +1,676 @@
#requires -Version 3.0 -Modules ConfigDefender, NetSecurity
<#
.SYNOPSIS
Bootstrap Microsoft Defender configuration
.DESCRIPTION
Bootstrap Microsoft Defender configuration, optimize and tweak Windows 10 protection and security
.PARAMETER CsvPath
The CSV with the configuration.
This is optional. Defaults are in the Script.
.PARAMETER Force
Enforce to apply the customize attack surface reduction rules
.EXAMPLE
PS C:\> .\Bootstrap-MicrosoftDefenderConfiguration.ps1
Bootstrap Microsoft Defender configuration, optimize and tweak Windows 10 Protection
.EXAMPLE
PS C:\> .\Bootstrap-MicrosoftDefenderConfiguration.ps1 -Verbose -Force
Bootstrap Microsoft Defender configuration, optimize and tweak Windows 10 Protection
.EXAMPLE
PS C:\> .\Bootstrap-MicrosoftDefenderConfiguration.ps1 -Verbose
Bootstrap Microsoft Defender configuration, optimize and tweak Windows 10 Protection
.LINK
https://docs.microsoft.com/en-us/powershell/module/defender/index?view=win10-ps
.LINK
https://github.com/jhochwald/PowerShell-collection/blob/master/Misc/Optimize-MicrosoftDefenderExclusions.ps1
.LINK
https://docs.microsoft.com/en-us/windows/security/threat-protection/microsoft-defender-atp/enable-exploit-protection
.LINK
https://docs.microsoft.com/en-us/powershell/module/defender/set-mppreference?view=win10-ps
.LINK
https://docs.microsoft.com/en-us/windows/security/threat-protection/microsoft-defender-atp/customize-attack-surface-reduction
.LINK
https://support.microsoft.com/en-us/help/822158/virus-scanning-recommendations-for-enterprise-computers-that-are-runni
.LINK
https://docs.microsoft.com/en-us/windows/security/threat-protection/windows-defender-antivirus/enable-cloud-protection-windows-defender-antivirus
.LINK
https://demo.wd.microsoft.com/?ocid=cx-wddocs-testground
.NOTES
Please review the settings, please tweak the rules file (or modify the default rule set here)
You need to run this in an elevated PowerShell!
I use this during the bootstrap process of Windows systems.
Most of the settings here is also enforced by some of our Group Policies and we also have a lot of it configured via MDM CSPs (InTune).
This script is only a quick hack to harden (and secure) any new system, even if it is not managed afterwords.
#>
[CmdletBinding(ConfirmImpact = 'Low',
SupportsShouldProcess)]
param
(
[Parameter(ValueFromPipeline,
ValueFromPipelineByPropertyName)]
[Alias('RulesCsv')]
[string]
$CsvPath = '.\Bootstrap-MicrosoftDefenderConfiguration.csv',
[Parameter(ValueFromPipeline,
ValueFromPipelineByPropertyName)]
[Alias('EnforceRule')]
[switch]
$Force = $null
)
begin
{
# Create a new Mail Object
$AttackSurfaceReductionRuleList = @()
#region CsvHandler
if (Test-Path -Path $CsvPath -ErrorAction SilentlyContinue)
{
#region ImportCsv
Write-Verbose -Message ('Import the attack surface reduction settings from ' + $CsvPath)
$AttackSurfaceReductionRuleList = (Import-Csv -Path $CsvPath -Delimiter ',' -Encoding UTF8)
#endregion ImportCsv
}
else
{
#region DefaultCsv
Write-Verbose -Message 'Use the attack surface reduction default settings'
# Create a virtual CSV File (Quick hack: To keep it plain and simple to maintain)
$RuleDefaults = 'RuleID,RuleDescription,RuleAction
75668C1F-73B5-4CF0-BB93-3ECF5CB7CC84, Block Office applications from injecting into other processes, Enabled
3B576869-A4EC-4529-8536-B80A7769E899, Block Office applications from creating executable content, Enabled
D4F940AB-401B-4EfC-AADC-AD5F3C50688A, Block Office applications from creating child processes, Enabled
D3E037E1-3EB8-44C8-A917-57927947596D, Impede JavaScript and VBScript to launch executable, Enabled
5BEB7EFE-FD9A-4556-801D-275E5FFC04CC, Block execution of potentially obfuscated script, Enabled
BE9BA2D9-53EA-4CDC-84E5-9B1EEEE46550, Block executable content from email client and webmail, Enabled
92E97FA1-2EDF-4476-BDD6-9DD0B4DDDC7B, Block Win32 imports from Macro code in Office, Enabled
c1db55ab-c21a-4637-bb3f-a12568109d35, Use advanced protection against ransomware, Enabled
9e6c4e1f-7d60-472f-ba1a-a39ef669e4b2, Block credential stealing from the Windows local security authority subsystem (lsass.exe), Enabled
d1e49aac-8f56-4280-b9ba-993a6d77406c, Block process creations originating from PSExec and WMI commands, Enabled
b2b3f03d-6a65-4f7b-a9c7-1c7ef74a9ba4, Block untrusted and unsigned processes that run from USB, Enabled
26190899-1602-49e8-8b27-eb1d0a1ce869, Block Office communication applications from creating child processes, AuditMode
7674ba52-37eb-4a4f-a9a1-f0f9a1619a2c, Block Adobe Reader from creating child processes, Enabled
e6db77e5-3df2-4cf1-b95a-636979351e5b, Block persistence through WMI event subscription, Enabled
01443614-cd74-433a-b99e-2ecdc07bfc25, Block executable files from running unless they meet a prevalence age or trusted list criteria, AuditMode'
# Import the virtual CSV File
$AttackSurfaceReductionRuleList = (ConvertFrom-Csv -InputObject $RuleDefaults -Delimiter ',')
#endregion DefaultCsv
}
#endregion CsvHandler
}
process
{
#region SetMpPreference
#region EnableNetworkProtection
Write-Verbose -Message 'Network protection helps to prevent employees from using any application to access dangerous domains that may host phishing scams, exploits, and other malicious content on the Internet'
$null = (Set-MpPreference -EnableNetworkProtection Enabled -Force -ErrorAction Continue)
#endregion EnableNetworkProtection
#region EnableControlledFolderAccess
Write-Verbose -Message 'Controlled folder access helps you protect valuable data from malicious apps and threats, such as ransomware'
$null = (Set-MpPreference -EnableControlledFolderAccess Enabled -Force -ErrorAction Continue)
#endregion EnableControlledFolderAccess
#region SignatureScheduleDay
Write-Verbose -Message 'Specifies the day of the week on which to check for definition updates'
$null = (Set-MpPreference -SignatureScheduleDay Everyday -Force -ErrorAction Continue)
#endregion SignatureScheduleDay
#region SignatureScheduleTime
Write-Verbose -Message 'Specifies the time of day, as the number of minutes after midnight, to check for definition updates'
$null = (Set-MpPreference -SignatureScheduleTime 320 -Force -ErrorAction Continue)
#endregion SignatureScheduleTime
#region DisableArchiveScanning
Write-Verbose -Message 'Indicates whether to scan archive files for malicious and unwanted software'
$null = (Set-MpPreference -DisableArchiveScanning $true -Force -ErrorAction Continue)
#endregion DisableArchiveScanning
#region DisableAutoExclusions
Write-Verbose -Message 'Indicates whether to disable the Automatic Exclusions feature for the server'
$null = (Set-MpPreference -DisableAutoExclusions $false -Force -ErrorAction Continue)
#endregion DisableAutoExclusions
#region DisableBehaviorMonitoring
Write-Verbose -Message 'Indicates whether to enable behavior monitoring'
$null = (Set-MpPreference -DisableBehaviorMonitoring $true -Force -ErrorAction Continue)
#endregion DisableBehaviorMonitoring
#region DisableBlockAtFirstSeen
Write-Verbose -Message 'Indicates whether to enable block at first seen'
$null = (Set-MpPreference -DisableBlockAtFirstSeen $true -Force -ErrorAction Continue)
#endregion DisableBlockAtFirstSeen
#region DisableCatchupFullScan
Write-Verbose -Message 'Indicates whether Windows Defender runs catch-up scans for scheduled full scans'
$null = (Set-MpPreference -DisableCatchupFullScan $true -Force -ErrorAction Continue)
#endregion DisableCatchupFullScan
#region DisableCatchupQuickScan
Write-Verbose -Message 'Indicates whether Windows Defender runs catch-up scans for scheduled quick scans'
$null = (Set-MpPreference -DisableCatchupQuickScan $true -Force -ErrorAction Continue)
#endregion DisableCatchupQuickScan
#region DisableEmailScanning
Write-Verbose -Message 'Indicates whether Windows Defender parses the mailbox and mail files, according to their specific format, in order to analyze mail bodies and attachments'
$null = (Set-MpPreference -DisableEmailScanning $false -Force -ErrorAction Continue)
#endregion DisableEmailScanning
#region DisableIOAVProtection
Write-Verbose -Message 'Indicates whether Windows Defender scans all downloaded files and attachments (e.g. Downloads)'
$null = (Set-MpPreference -DisableIOAVProtection $true -Force -ErrorAction Continue)
#endregion DisableIOAVProtection
#region DisableIntrusionPreventionSystem
Write-Verbose -Message 'Indicates whether to configure network protection against exploitation of known vulnerabilities'
$null = (Set-MpPreference -DisableIntrusionPreventionSystem $false -Force -ErrorAction Continue)
#endregion DisableIntrusionPreventionSystem
#region DisablePrivacyMode
Write-Verbose -Message 'Indicates whether to disable privacy mode. Privacy mode prevents users, other than administrators, from displaying threat history'
$null = (Set-MpPreference -DisablePrivacyMode $false -Force -ErrorAction Continue)
#endregion DisablePrivacyMode
#region DisableRealtimeMonitoring
Write-Verbose -Message 'Indicates whether to use real-time protection'
$null = (Set-MpPreference -DisableRealtimeMonitoring $false -Force -ErrorAction Continue)
#endregion DisableRealtimeMonitoring
#region CheckForSignaturesBeforeRunningScan
Write-Verbose -Message 'Enable checking signatures before scanning'
$null = (Set-MpPreference -CheckForSignaturesBeforeRunningScan 1 -Force -ErrorAction Continue)
#endregion CheckForSignaturesBeforeRunningScan
#region DisableRemovableDriveScanning
Write-Verbose -Message 'Indicates whether to scan for malicious and unwanted software in removable drives, such as flash drives, during a full scan'
$null = (Set-MpPreference -DisableRemovableDriveScanning $true -Force -ErrorAction Continue)
#endregion DisableRemovableDriveScanning
#region DisableRestorePoint
Write-Verbose -Message 'Indicates whether to disable scanning of restore points'
$null = (Set-MpPreference -DisableRestorePoint $true -Force -ErrorAction Continue)
#endregion DisableRestorePoint
#region DisableScanningMappedNetworkDrivesForFullScan
Write-Verbose -Message 'Indicates whether to scan mapped network drives'
$null = (Set-MpPreference -DisableScanningMappedNetworkDrivesForFullScan $true -Force -ErrorAction Continue)
#endregion DisableScanningMappedNetworkDrivesForFullScan
#region DisableScanningNetworkFiles
Write-Verbose -Message 'Indicates whether to scan for network files'
$null = (Set-MpPreference -DisableScanningNetworkFiles $false -Force -ErrorAction Continue)
#endregion DisableScanningNetworkFiles
#region DisableScriptScanning
Write-Verbose -Message 'Specifies whether to disable the scanning of scripts during malware scans'
$null = (Set-MpPreference -DisableScriptScanning $false -Force -ErrorAction Continue)
#endregion DisableScriptScanning
#region HighThreatDefaultAction
Write-Verbose -Message 'Specifies which automatic remediation action to take for a high level threat'
$null = (Set-MpPreference -HighThreatDefaultAction Quarantine -Force -ErrorAction Continue)
#endregion HighThreatDefaultAction
#region LowThreatDefaultAction
Write-Verbose -Message 'Specifies which automatic remediation action to take for a low level threat'
$null = (Set-MpPreference -LowThreatDefaultAction Block -Force -ErrorAction Continue)
#endregion LowThreatDefaultAction
#region ModerateThreatDefaultAction
Write-Verbose -Message 'Specifies which automatic remediation action to take for a moderate level threat'
$null = (Set-MpPreference -ModerateThreatDefaultAction Quarantine -Force -ErrorAction Continue)
#endregion ModerateThreatDefaultAction
#region PUAProtection
Write-Verbose -Message 'Disable PUA Protection'
$null = (Set-MpPreference -PUAProtection Enabled -Force -ErrorAction Continue)
#endregion PUAProtection
#region QuarantinePurgeItemsAfterDelay
Write-Verbose -Message 'Specifies the number of days to keep items in the Quarantine folder'
$null = (Set-MpPreference -QuarantinePurgeItemsAfterDelay 30 -Force -ErrorAction Continue)
#endregion QuarantinePurgeItemsAfterDelay
#region RandomizeScheduleTaskTimes
Write-Verbose -Message 'Indicates whether to select a random time for the scheduled start and scheduled update for definitions'
$null = (Set-MpPreference -RandomizeScheduleTaskTimes $true -Force -ErrorAction Continue)
#endregion RandomizeScheduleTaskTimes
#region RealTimeScanDirection
Write-Verbose -Message 'Specifies scanning configuration for incoming and outgoing files on NTFS volumes'
$null = (Set-MpPreference -RealTimeScanDirection 0 -Force -ErrorAction Continue)
#endregion RealTimeScanDirection
#region RemediationScheduleDay
Write-Verbose -Message 'Specifies the day of the week on which to perform a scheduled full scan in order to complete remediation'
$null = (Set-MpPreference -RemediationScheduleDay Everyday -Force -ErrorAction Continue)
#endregion
#region RemediationScheduleTime
Write-Verbose -Message 'Specifies the time of day, as the number of minutes after midnight, to perform a scheduled scan'
$null = (Set-MpPreference -RemediationScheduleTime 120 -Force -ErrorAction Continue)
#endregion RemediationScheduleDay
#region ReportingAdditionalActionTimeOut
Write-Verbose -Message 'Specifies the number of minutes before a detection in the additional action state changes to the cleared state'
$null = (Set-MpPreference -ReportingAdditionalActionTimeOut 10080 -Force -ErrorAction Continue)
#endregion ReportingAdditionalActionTimeOut
#region ReportingCriticalFailureTimeOut
Write-Verbose -Message 'Specifies the number of minutes before a detection in the critically failed state changes to either the additional action state or the cleared state'
$null = (Set-MpPreference -ReportingCriticalFailureTimeOut 10080 -Force -ErrorAction Continue)
#endregion ReportingCriticalFailureTimeOut
#region ReportingNonCriticalTimeOut
Write-Verbose -Message 'Specifies the number of minutes before a detection in the non-critically failed state changes to the cleared state'
$null = (Set-MpPreference -ReportingNonCriticalTimeOut 11440 -Force -ErrorAction Continue)
#endregion ReportingNonCriticalTimeOut
#region ScanAvgCPULoadFactor
Write-Verbose -Message 'Specifies the maximum percentage CPU usage for a scan'
$null = (Set-MpPreference -ScanAvgCPULoadFactor 50 -Force -ErrorAction Continue)
#endregion ScanAvgCPULoadFactor
#region ScanOnlyIfIdleEnabled
Write-Verbose -Message 'Indicates whether to start scheduled scans only when the computer is not in use'
$null = (Set-MpPreference -ScanOnlyIfIdleEnabled $true -Force -ErrorAction Continue)
#endregion ScanOnlyIfIdleEnabled
#region ScanParameters
Write-Verbose -Message 'Specifies the scan type to use during a scheduled scan'
$null = (Set-MpPreference -ScanParameters 1 -Force -ErrorAction Continue)
#endregion ScanParameters
#region ScanPurgeItemsAfterDelay
Write-Verbose -Message 'Specifies the number of days to keep items in the scan history folder'
$null = (Set-MpPreference -ScanPurgeItemsAfterDelay 15 -Force -ErrorAction Continue)
#endregion ScanPurgeItemsAfterDelay
#region ScanScheduleDay
Write-Verbose -Message 'Specifies the day of the week on which to perform a scheduled scan'
$null = (Set-MpPreference -ScanScheduleDay Everyday -Force -ErrorAction Continue)
#endregion ScanScheduleDay
#region ScanScheduleQuickScanTime
Write-Verbose -Message 'Specifies the time of day, as the number of minutes after midnight, to perform a scheduled quick scan'
$null = (Set-MpPreference -ScanScheduleQuickScanTime 0 -Force -ErrorAction Continue)
#endregion ScanScheduleQuickScanTime
#region ScanScheduleTime
Write-Verbose -Message 'Specifies the time of day, as the number of minutes after midnight, to perform a scheduled scan'
$null = (Set-MpPreference -ScanScheduleTime 120 -Force -ErrorAction Continue)
#endregion ScanScheduleTime
#region SevereThreatDefaultAction
Write-Verbose -Message 'Specifies which automatic remediation action to take for a severe level threat'
$null = (Set-MpPreference -SevereThreatDefaultAction Quarantine -Force -ErrorAction Continue)
#endregion SevereThreatDefaultAction
#region SignatureAuGracePeriod
Write-Verbose -Message 'Specifies a grace period, in minutes, for the definition'
$null = (Set-MpPreference -SignatureAuGracePeriod 0 -Force -ErrorAction Continue)
#endregion SignatureAuGracePeriod
#region SignatureDisableUpdateOnStartupWithoutEngine
Write-Verbose -Message 'Indicates whether to initiate definition updates even if no antimalware engine is present'
$null = (Set-MpPreference -SignatureDisableUpdateOnStartupWithoutEngine $false -Force -ErrorAction Continue)
#endregion SignatureDisableUpdateOnStartupWithoutEngine
#region SignatureFirstAuGracePeriod
Write-Verbose -Message 'Specifies a grace period, in minutes, for the definition. If a definition successfully updates within this period, Windows Defender abandons any service initiated updates'
$null = (Set-MpPreference -SignatureFirstAuGracePeriod 120 -Force -ErrorAction Continue)
#endregion SignatureFirstAuGracePeriod
#region SignatureScheduleDay
Write-Verbose -Message 'Specifies the day of the week on which to check for definition updates'
$null = (Set-MpPreference -SignatureScheduleDay Everyday -Force -ErrorAction Continue)
#endregion SignatureScheduleDay
#region SignatureScheduleTime
Write-Verbose -Message 'Specifies the time of day, as the number of minutes after midnight, to check for definition updates'
$null = (Set-MpPreference -SignatureScheduleTime 165 -Force -ErrorAction Continue)
#endregion SignatureScheduleTime
#region SignatureUpdateCatchupInterval
Write-Verbose -Message 'Specifies the number of days after which Windows Defender requires a catch-up definition update'
$null = (Set-MpPreference -SignatureUpdateCatchupInterval 1 -Force -ErrorAction Continue)
#endregion SignatureUpdateCatchupInterval
#region SignatureUpdateInterval
Write-Verbose -Message 'Specifies the interval, in hours, at which to check for definition updates'
$null = (Set-MpPreference -SignatureUpdateInterval 12 -Force -ErrorAction Continue)
#endregion SignatureUpdateInterval
#region SubmitSamplesConsent
Write-Verbose -Message 'Specifies how Windows Defender checks for user consent for certain samples'
$null = (Set-MpPreference -SubmitSamplesConsent AlwaysPrompt -Force -ErrorAction Continue)
#endregion SubmitSamplesConsent
#region MAPSReporting MAPSReporting
Write-Verbose -Message 'Membership in Microsoft Active Protection Service Enable'
$null = (Set-MpPreference -MAPSReporting Advanced -Force -ErrorAction Continue)
#endregion MAPSReporting MAPSReporting
#region ThrottleLimit
Write-Verbose -Message 'Specifies the maximum number of concurrent operations that can be established to run the cmdlet'
$null = (Set-MpPreference -ThrottleLimit 0 -Force -ErrorAction Continue)
#endregion ThrottleLimit
#region UILockdown
Write-Verbose -Message 'Indicates whether to disable UI lock down mode'
$null = (Set-MpPreference -UILockdown $false -Force -ErrorAction Continue)
#endregion UILockdown
#region UnknownThreatDefaultAction
Write-Verbose -Message 'Specifies which automatic remediation action to take for an unknown level threat'
$null = (Set-MpPreference -UnknownThreatDefaultAction Block -Force -ErrorAction Continue)
#endregion UnknownThreatDefaultAction
#region SignatureFallbackOrder
Write-Verbose -Message 'Specifies the order in which to contact different definition update sources.'
$null = (Set-MpPreference -SignatureFallbackOrder 'MicrosoftUpdateServer | MMPC' -Force -ErrorAction Continue)
#endregion SignatureFallbackOrder
#region ControlledFolderAccessAllowedApplications
Write-Verbose -Message 'Setup the Controlled Folder Access Allowed Applications'
# Define a list of Applications to exclude - Fully Qualified
<#
I like to keep this list as short as possible
#>
$NewControlledFolderAccessAllowedApplications = @(
"$env:windir\System32\taskhostw.exe"
)
# Create a new Object
$AllControlledFolderAccessAllowedApplications = (New-Object -TypeName System.Collections.Generic.List[System.Object])
# Get the existing exclusions
$AllControlledFolderAccessAllowedApplications.Add((Get-MpPreference -ErrorAction SilentlyContinue | Select-Object -ExpandProperty ControlledFolderAccessAllowedApplications))
#region NewControlledFolderAccessAllowedApplicationsLoop
foreach ($NewControlledFolderAccessAllowedApplication in $NewControlledFolderAccessAllowedApplications)
{
if ($AllControlledFolderAccessAllowedApplications -notcontains $NewControlledFolderAccessAllowedApplication)
{
Write-Verbose -Message ('Add ' + $NewControlledFolderAccessAllowedApplication + ' to the Controlled Folder Access Allowed Applications list')
$AllControlledFolderAccessAllowedApplications.Add($NewControlledFolderAccessAllowedApplication)
}
}
#endregion NewControlledFolderAccessAllowedApplicationsLoop
# Make sure we have nothing doubled
$AllControlledFolderAccessAllowedApplications = ($AllControlledFolderAccessAllowedApplications | Sort-Object -Unique)
# Apply the new exclusion list. This will replace the complete list.
Write-Verbose -Message 'Apply the new Controlled Folder Access Allowed Applications list'
$null = (Set-MpPreference -ControlledFolderAccessAllowedApplications $AllControlledFolderAccessAllowedApplications -Force -ErrorAction Continue)
#endregion ControlledFolderAccessAllowedApplications
#region ExclusionPath
# Define a list of Applications to exclude - Fully Qualified
$NewExclusionPathList = @(
"$env:windir\SoftwareDistribution\DataStore\Datastore.edb",
"$env:windir\SoftwareDistribution\DataStore\Logs\Edb*.jrs",
"$env:windir\SoftwareDistribution\DataStore\Logs\Edb.chk",
"$env:windir\SoftwareDistribution\DataStore\Logs\Tmp.edb",
"$env:windir\Security\Database\*.edb",
"$env:windir\Security\Database\*.sdb",
"$env:windir\Security\Database\*.log",
"$env:windir\Security\Database\*.chk",
"$env:windir\Security\Database\*.jrs",
"$env:windir\Security\Database\*.xml",
"$env:windir\Security\Database\*.csv",
"$env:windir\Security\Database\*.cmtx",
"$env:ProgramData\ntuser.pol",
"$env:windir\System32\GroupPolicy\Machine\Registry.pol",
"$env:windir\System32\GroupPolicy\Machine\Registry.tmp",
"$env:windir\System32\GroupPolicy\User\Registry.pol",
"$env:windir\System32\GroupPolicy\User\Registry.tmp"
)
# Create a new Object
$AllExclusionPath = (New-Object -TypeName System.Collections.Generic.List[System.Object])
# Get the existing exclusions
$AllExclusionPath.Add((Get-MpPreference -ErrorAction SilentlyContinue | Select-Object -ExpandProperty ExclusionPath))
#region NewExclusionPathLoop
foreach ($NewExclusionPath in $NewExclusionPathList)
{
if ($AllExclusionPath -notcontains $NewExclusionPath)
{
Write-Verbose -Message ('Add ' + $NewExclusionPath + ' as path to exclude')
$AllExclusionPath.Add($NewExclusionPath)
}
}
#endregion NewExclusionPathLoop
# Make sure we have nothing doubled
$AllExclusionPath = ($AllExclusionPath | Sort-Object -Unique)
# Apply the new exclusion list. This will replace the complete list.
Write-Verbose -Message 'Apply the new Path to exclude list'
$null = (Set-MpPreference -ExclusionPath $AllExclusionPath -Force -ErrorAction Continue)
#endregion ExclusionPath
#region ExclusionProcess
# Define a list of Applications to exclude - Fully Qualified
$NewExclusionProcessList = @(
"$env:windir\System32\svchost.exe",
"$env:windir\System32\wuauclt.exe"
)
# Create a new Object
$AllExclusionProcess = (New-Object -TypeName System.Collections.Generic.List[System.Object])
# Get the existing exclusions
$AllExclusionProcess.Add((Get-MpPreference -ErrorAction SilentlyContinue | Select-Object -ExpandProperty ExclusionProcess))
#region NewExclusionProcessLoop
foreach ($NewExclusionProcess in $NewExclusionProcessList)
{
if ($AllExclusionProcess -notcontains $NewExclusionProcess)
{
Write-Verbose -Message ('Add ' + $NewExclusionProcess + ' as process to exclude')
$AllExclusionProcess.Add($NewExclusionProcess)
}
}
#endregion NewExclusionProcessLoop
# Make sure we have nothing doubled
$AllExclusionProcess = ($AllExclusionProcess | Sort-Object -Unique)
# Apply the new exclusion list. This will replace the complete list.
Write-Verbose -Message 'Apply the new Process to exclude list'
$null = (Set-MpPreference -ExclusionProcess $AllExclusionProcess -Force -ErrorAction Continue)
#endregion ExclusionProcess
#endregion SetMpPreference
#region ProcessMitigation
# Local Process Mitigation file
$ProcessMitigationFile = '.\ProcessMitigation.xml'
# Check if we have a local Process Mitigation file
if (-not (Test-Path -Path $ProcessMitigationFile -ErrorAction SilentlyContinue))
{
# Where to download the XML File?
$ProcessMitigationUri = 'https://demo.wd.microsoft.com/Content/ProcessMitigation.xml'
Write-Verbose -Message ('Downloading Process Mitigation file from ' + $ProcessMitigationUri)
# Download
$paramInvokeWebRequest = @{
Uri = $ProcessMitigationUri
OutFile = $ProcessMitigationFile
Method = 'Get'
ContentType = 'text/xml'
ErrorAction = 'Continue'
}
$null = (Invoke-WebRequest @paramInvokeWebRequest)
}
if (Test-Path -Path $ProcessMitigationFile -ErrorAction SilentlyContinue)
{
Write-Verbose -Message 'Enabling Exploit Protection'
# Apply the File
$null = (Set-ProcessMitigation -PolicyFilePath $ProcessMitigationFile -ErrorAction Continue)
# Cleanup
$paramRemoveItem = @{
Path = $ProcessMitigationFile
Force = $true
Confirm = $false
ErrorAction = 'Continue'
}
$null = (Remove-Item @paramRemoveItem)
}
else
{
Write-Warning -Message ('The local Process Mitigation file (' + $ProcessMitigationFile + ') is missing! Not enabling Exploit Protection.')
}
#endregion ProcessMitigation
#region WindowsDefenderSandbox
Write-Verbose -Message 'Turn on Windows Defender Sandbox'
$null = ([Environment]::SetEnvironmentVariable('MP_FORCE_USE_SANDBOX', 1, 'Machine'))
#endregion WindowsDefenderSandbox
#region AttackSurfaceReduction
#region GetAttackSurfaceReductionRulesIds
$AttackSurfaceReductionRulesIds = (Get-MpPreference -ErrorAction SilentlyContinue | Select-Object -ExpandProperty AttackSurfaceReductionRules_Ids)
#endregion GetAttackSurfaceReductionRulesIds
Write-Verbose -Message 'Enabling Attack Surface Reduction rules'
#region SetMpPreferenceDefaults
$AddMpPreferenceParameters = @{
ErrorAction = 'Stop'
Force = $true
}
#endregion SetMpPreferenceDefaults
#region RuleLoop
foreach ($AttackSurfaceReductionRule in $AttackSurfaceReductionRuleList)
{
#region SingleLoop
try
{
if (($Force) -or ($AttackSurfaceReductionRulesIds -notcontains $AttackSurfaceReductionRule.RuleID))
{
#region AppleTheRuleValue
Write-Verbose -Message ('Set ' + $AttackSurfaceReductionRule.RuleDescription + ' to ' + $AttackSurfaceReductionRule.RuleAction)
# Add some values
$AddMpPreferenceParameters.AttackSurfaceReductionRules_Ids = $AttackSurfaceReductionRule.RuleID
$AddMpPreferenceParameters.AttackSurfaceReductionRules_Actions = $AttackSurfaceReductionRule.RuleAction
# Apply the Rule
$null = (Add-MpPreference @AddMpPreferenceParameters)
#endregion AppleTheRuleValue
}
}
catch
{
#region ErrorHandler
# get error record
[Management.Automation.ErrorRecord]$e = $_
# retrieve information about runtime error
$info = [PSCustomObject]@{
Exception = $e.Exception.Message
Reason = $e.CategoryInfo.Reason
Target = $e.CategoryInfo.TargetName
Script = $e.InvocationInfo.ScriptName
Line = $e.InvocationInfo.ScriptLineNumber
Column = $e.InvocationInfo.OffsetInLine
}
$info | Out-String | Write-Verbose
Write-Warning -Message ('Unable to enable to Rule: ' + $AttackSurfaceReductionRule.RuleID + ' (' + $AttackSurfaceReductionRule.RuleDescription + ')')
#endregion ErrorHandler
}
#endregion SingleLoop
}
#endregion RuleLoop
#endregion AttackSurfaceReduction
#region ReloadRegistry
& "$env:windir\system32\rundll32.exe" USER32.DLL, UpdatePerUserSystemParameters , 1 , True
#endregion ReloadRegistry
#region EnableFirewall
Write-Verbose -Message 'Enable the Windows Firewall for all Profiles - Set the default to block everything'
$null = (Set-NetFirewallProfile -Profile Domain, Public, Private -Enabled True -DefaultInboundAction Block -LogBlocked True -Confirm:$false -ErrorAction Continue)
#endregion EnableFirewall
}
end
{
#region UpdateSignature
Write-Verbose -Message 'Update Defender'
$null = (Update-MpSignature)
#endregion UpdateSignature
}
#region LICENSE
<#
BSD 3-Clause License
Copyright (c) 2021, enabling Technology
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#>
#endregion LICENSE
#region DISCLAIMER
<#
DISCLAIMER:
- Use at your own risk, etc.
- This is open-source software, if you find an issue try to fix it yourself. There is no support and/or warranty in any kind
- This is a third-party Software
- The developer of this Software is NOT sponsored by or affiliated with Microsoft Corp (MSFT) or any of its subsidiaries in any way
- The Software is not supported by Microsoft Corp (MSFT)
- By using the Software, you agree to the License, Terms, and any Conditions declared and described above
- If you disagree with any of the terms, and any conditions declared: Just delete it and build your own solution
#>
#endregion DISCLAIMER

View File

@@ -0,0 +1,29 @@
BSD 3-Clause License
Copyright (c) 2021, enabling Technology <http://enatec.io>
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@@ -0,0 +1,476 @@
# Bootstrap Microsoft Defender configuration
Bootstrap Microsoft Defender configuration, optimize and tweak Windows 10 protection and security
## What it does
Several Microsoft Defender settings are configured.
### EnableNetworkProtection
Network protection helps to prevent employees from using any application to access dangerous domains that may host phishing scams, exploits, and other malicious content on the Internet
Set to: `Enabled`
### EnableControlledFolderAccess
Controlled folder access helps you protect valuable data from malicious apps and threats, such as ransomware
Set to: `Enabled`
### SignatureScheduleDay
Specifies the day of the week on which to check for definition updates.
Set to: `Everyday`
### SignatureScheduleTime
Specifies the time of day, as the number of minutes after midnight, to check for definition updates
Set to: `320`
### DisableArchiveScanning
Indicates whether to scan archive files for malicious and unwanted software
Set to: `true`
### DisableAutoExclusions
Indicates whether to disable the Automatic Exclusions feature for the server
Set to: `false`
### DisableBehaviorMonitoring
Indicates whether to enable behavior monitoring
Set to: `true`
Something I enable on a few systems only.
### DisableBlockAtFirstSeen
Indicates whether to enable block at first seen
Set to: `true`
### DisableCatchupFullScan
Indicates whether Windows Defender runs catch-up scans for scheduled full scans
Set to: `true`
### DisableCatchupQuickScan
Indicates whether Windows Defender runs catch-up scans for scheduled quick scans
Set to: `true`
### DisableEmailScanning
Indicates whether Windows Defender parses the mailbox and mail files, according to their specific format, in order to analyze mail bodies and attachments
Set to: `false`
### DisableIOAVProtection
Indicates whether Windows Defender scans all downloaded files and attachments (e.g. Downloads)
Set to: `true`
### DisableIntrusionPreventionSystem
Indicates whether to configure network protection against exploitation of known vulnerabilities
Set to: `false`
### DisablePrivacyMode
Indicates whether to disable privacy mode. Privacy mode prevents users, other than administrators, from displaying threat history
Set to: `false`
### DisableRealtimeMonitoring
Indicates whether to use real-time protection
Set to: `false`
### CheckForSignaturesBeforeRunningScan
Set to: `1`
### DisableRemovableDriveScanning
Indicates whether to scan for malicious and unwanted software in removable drives, such as flash drives, during a full scan
Set to: `true`
### DisableRestorePoint
Indicates whether to disable scanning of restore points
Set to: `true`
### DisableScanningMappedNetworkDrivesForFullScan
Indicates whether to scan mapped network drives
Set to: `true`
### DisableScanningNetworkFiles
Indicates whether to scan for network files
Set to: `false`
### DisableScriptScanning
Specifies whether to disable the scanning of scripts during malware scans
Set to: `false`
### HighThreatDefaultAction
Specifies which automatic remediation action to take for a high level threat
Set to: `Quarantine`
### LowThreatDefaultAction
Specifies which automatic remediation action to take for a low level threat
Set to: `Block`
### ModerateThreatDefaultAction
Specifies which automatic remediation action to take for a moderate level threat
Set to: `Quarantine`
### PUAProtection
Disable PUA Protection
Set to: `Enabled`
### QuarantinePurgeItemsAfterDelay
Specifies the number of days to keep items in the Quarantine folder
Set to: `30`
### RandomizeScheduleTaskTimes
Indicates whether to select a random time for the scheduled start and scheduled update for definitions
Set to: `true`
### RealTimeScanDirection
Specifies scanning configuration for incoming and outgoing files on NTFS volumes
Set to: `0`
### RemediationScheduleDay
Specifies the day of the week on which to perform a scheduled full scan in order to complete remediation
Set to: `Everyday`
### RemediationScheduleTime
Specifies the time of day, as the number of minutes after midnight, to perform a scheduled scan
Set to: `120`
### ReportingAdditionalActionTimeOut
Specifies the number of minutes before a detection in the additional action state changes to the cleared state
Set to: `10080`
### ReportingCriticalFailureTimeOut
Specifies the number of minutes before a detection in the critically failed state changes to either the additional action state or the cleared state
Set to: `10080`
### ReportingNonCriticalTimeOut
Specifies the number of minutes before a detection in the non-critically failed state changes to the cleared state
Set to: `11440`
### ScanAvgCPULoadFactor
Specifies the maximum percentage CPU usage for a scan
Set to: `50`
### ScanOnlyIfIdleEnabled
Indicates whether to start scheduled scans only when the computer is not in use
Set to: `true`
### ScanParameters
Specifies the scan type to use during a scheduled scan
Set to: `1`
### ScanPurgeItemsAfterDelay
Specifies the number of days to keep items in the scan history folder
Set to: `15`
### ScanScheduleDay
Specifies the day of the week on which to perform a scheduled scan
Set to: `Everyday`
### ScanScheduleQuickScanTime
Specifies the time of day, as the number of minutes after midnight, to perform a scheduled quick scan
Set to: `0`
### ScanScheduleTime
Specifies the time of day, as the number of minutes after midnight, to perform a scheduled scan
Set to: `120`
### SevereThreatDefaultAction
Specifies which automatic remediation action to take for a severe level threat
Set to: `Quarantine`
### SignatureAuGracePeriod
Specifies a grace period, in minutes, for the definition
Set to: `0`
### SignatureDisableUpdateOnStartupWithoutEngine
Indicates whether to initiate definition updates even if no antimalware engine is present
Set to: `false`
### SignatureFirstAuGracePeriod
Specifies a grace period, in minutes, for the definition. If a definition successfully updates within this period, Windows Defender abandons any service initiated updates
Set to: `120`
### SignatureScheduleDay
Specifies the day of the week on which to check for definition updates
Set to: `Everyday`
### SignatureScheduleTime
Specifies the time of day, as the number of minutes after midnight, to check for definition updates
Set to: `165`
### SignatureUpdateCatchupInterval
Specifies the number of days after which Windows Defender requires a catch-up definition update
Set to: `1`
### SignatureUpdateInterval
Specifies the interval, in hours, at which to check for definition updates
Set to: `12`
### SubmitSamplesConsent
Specifies how Windows Defender checks for user consent for certain samples
Set to: `AlwaysPrompt`
### MAPSReporting
Membership in Microsoft Active Protection Service Enable
Set to: `Advanced`
### ThrottleLimit
Specifies the maximum number of concurrent operations that can be established to run the cmdlet
Set to: `0`
### UILockdown
Indicates whether to disable UI lock down mode
Set to: `false`
### UnknownThreatDefaultAction
Specifies which automatic remediation action to take for an unknown level threat
Set to: `Block`
### SignatureFallbackOrder
Specifies the order in which to contact different definition update sources. Specify the types of update sources in the order in which you want Windows Defender to contact them, enclosed in braces and separated by the pipeline symbol
Set to: `MicrosoftUpdateServer | MMPC`
### ControlledFolderAccessAllowedApplications
We exclude the following Files by default: `$env:windir\System32\taskhostw.exe`
Any exclusions previously configured stay intact!
### ExclusionPath
The following Files/Folders are excluded from the scan:
`windir\SoftwareDistribution\DataStore\Datastore.edb`
`windir\SoftwareDistribution\DataStore\Logs\Edb*.jrs`
`windir\SoftwareDistribution\DataStore\Logs\Edb.chk`
`windir\SoftwareDistribution\DataStore\Logs\Tmp.edb`
`windir\Security\Database\*.edb`
`windir\Security\Database\*.sdb`
`windir\Security\Database\*.log`
`windir\Security\Database\*.chk`
`windir\Security\Database\*.jrs`
`windir\Security\Database\*.xml`
`windir\Security\Database\*.csv`
`windir\Security\Database\*.cmtx`
`ProgramData\ntuser.pol`
`windir\System32\GroupPolicy\Machine\Registry.pol`
`windir\System32\GroupPolicy\Machine\Registry.tmp`
`windir\System32\GroupPolicy\User\Registry.pol`
`windir\System32\GroupPolicy\User\Registry.tmp`
Any exclusions previously configured stay intact!
More Info: [https://support.microsoft.com/en-us/help/822158/virus-scanning-recommendations-for-enterprise-computers-that-are-runni](https://support.microsoft.com/en-us/help/822158/virus-scanning-recommendations-for-enterprise-computers-that-are-runni)
### ExclusionProcess
The following processes are excluded from the scan:
`$env:windir\System32\svchost.exe`
`$env:windir\System32\wuauclt.exe`
Any exclusions previously configured stay intact!
More Info: [https://support.microsoft.com/en-us/help/822158/virus-scanning-recommendations-for-enterprise-computers-that-are-runni](https://support.microsoft.com/en-us/help/822158/virus-scanning-recommendations-for-enterprise-computers-that-are-runni)
### Process Mitigation and Exploit Protection
Microsoft provides a XML file (`ProcessMitigation.xml`) that provides a configuration best practice to mitigate the attack surface and provide Exploit Protection.
You can provide your own File, otherwise (if missing) we will download the latest version from Microsoft.
More Info: [https://demo.wd.microsoft.com/Page/EP](https://demo.wd.microsoft.com/Page/EP)
### WindowsDefenderSandbox
We tuen on Windows Defender Sandbox
### AttackSurfaceReduction
Attack Surface Reduction (ASR) is comprised of a number of rules, each of which target specific behaviors that are typically used by malware and malicious apps to infect machines, such as:
- Executable files and scripts used in Office apps or web mail that attempt to download or run files
- Scripts that are obfuscated or otherwise suspicious
- Behaviors that apps undertake that are not usually initiated during normal day-to-day work
More Info: [https://demo.wd.microsoft.com/Page/ASR](https://demo.wd.microsoft.com/Page/ASR)
### ReloadRegistry
We then reload the registry to ensure that the new configuration is activated
### EnableFirewall
Enable the Windows Firewall for all Profiles - Set the default to block everything
We enable the Windows Firewall for the following Network-Profiles:
- Domain
- Public
- Private
We block all inbound connections by default and we log all block-events!
### UpdateSignature
As a final touch: We update the Windows Defender signatures.
## Why this?
I use this during the bootstrap process of Windows systems.
Most of the settings here is also enforced by some of our Group Policies and we also have a lot of it configured via MDM CSPs (InTune).
This script is only a quick hack to harden (and secure) any new system, even if it is not managed afterwords.
## Content
There are two files:
### Bootstrap-MicrosoftDefenderConfiguration.ps1
The PowerShell Script itself
### Bootstrap-MicrosoftDefenderConfiguration.csv
A CSV File that contains the configuration of the attack surface reduction rules.
## Configuration
Please review the `Bootstrap-MicrosoftDefenderConfiguration.csv` where you configure the attack surface reduction rules. Please also review the `Bootstrap-MicrosoftDefenderConfiguration.ps1` file. There is no configuration file, at least not yet!
## Further Information
[https://docs.microsoft.com/en-us/powershell/module/defender/index?view=win10-ps](https://docs.microsoft.com/en-us/powershell/module/defender/index?view=win10-ps)
[https://github.com/jhochwald/PowerShell-collection/blob/master/Misc/Optimize-MicrosoftDefenderExclusions.ps1](https://github.com/jhochwald/PowerShell-collection/blob/master/Misc/Optimize-MicrosoftDefenderExclusions.ps1)
[https://docs.microsoft.com/en-us/windows/security/threat-protection/microsoft-defender-atp/enable-exploit-protection](https://docs.microsoft.com/en-us/windows/security/threat-protection/microsoft-defender-atp/enable-exploit-protection
)
[https://docs.microsoft.com/en-us/powershell/module/defender/set-mppreference?view=win10-ps](https://docs.microsoft.com/en-us/powershell/module/defender/set-mppreference?view=win10-ps)
[https://docs.microsoft.com/en-us/windows/security/threat-protection/microsoft-defender-atp/customize-attack-surface-reduction](https://docs.microsoft.com/en-us/powershell/module/defender/set-mppreference?view=win10-ps)
[https://support.microsoft.com/en-us/help/822158/virus-scanning-recommendations-for-enterprise-computers-that-are-runni](https://docs.microsoft.com/en-us/powershell/module/defender/set-mppreference?view=win10-ps)
[https://docs.microsoft.com/en-us/windows/security/threat-protection/windows-defender-antivirus/enable-cloud-protection-windows-defender-antivirus](https://docs.microsoft.com/en-us/powershell/module/defender/set-mppreference?view=win10-ps)
[https://demo.wd.microsoft.com/?ocid=cx-wddocs-testground](https://docs.microsoft.com/en-us/powershell/module/defender/set-mppreference?view=win10-ps)
## License
BSD 3-Clause License
Copyright (c) 2020, Joerg Hochwald
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@@ -0,0 +1,158 @@
#requires -Version 3.0 -RunAsAdministrator
<#
.SYNOPSIS
Quick an dirty Windows Service Monitor
.DESCRIPTION
I came across the the problem, that one of the services I depend one was not started after the system reboots.
That happens after a .NET update. So I decided to create this real simple monitor to make sure, that this service is running.
If not, the script tries to restart it.
.PARAMETER MonService
The Service we would like to check. Default is RoyalServer
.EXAMPLE
PS C:\> .\Check-ServiceMonitor.ps1
.EXAMPLE
PS C:\> .\Check-ServiceMonitor.ps1 -MonService 'myservice'
.NOTES
The script itself have some basic error handling,
nothing to complex or fancy.
#>
[CmdletBinding(ConfirmImpact = 'None')]
param
(
[Parameter(ValueFromPipeline,
Position = 1)]
[Alias('ServiceToMonitor')]
[string]
$MonService = 'RoyalServer'
)
begin
{
[string]$SC = 'SilentlyContinue'
[string]$STP = 'Stop'
}
process
{
# Get the Status
try
{
Write-Verbose -Message ('Get the Status of {0}' -f $MonService)
$paramGetService = @{
Name = $MonService
ErrorAction = $STP
WarningAction = $SC
}
[string]$MonServiceStatus = ((Get-Service @paramGetService).Status)
Write-Verbose -Message ('We have the Status of {0}' -f $MonService)
}
catch
{
Write-Error -Message ('Looks like the Service {0} is not installed!' -f $MonService) -ErrorAction $STP
# Point of no return (Should never be reached)
break
}
# Do the check
if ($MonServiceStatus -ne 'Running')
{
Write-Warning -Message ('Sorry, but {0} is not running ' -f $MonService)
try
{
Write-Verbose -Message ('Try to restart {0}' -f $MonService)
$MonParam = @{
Name = $MonService
Confirm = $false
ErrorAction = $STP
WarningAction = $SC
}
$null = (Restart-Service @MonParam)
}
catch
{
# Whooooops! Try it again... Let us try to stop the services
Write-Verbose -Message ('Try to stop {0}' -f $MonService)
$null = (Stop-Service @MonParam)
# Wait a second
$null = (Start-Sleep -Seconds 1)
# Try to stop it again...
$null = (Stop-Service @MonParam)
# Wait a second
$null = (Start-Sleep -Seconds 1)
# Try to kill it, again!
$null = (Stop-Service @MonParam)
# Wait two seconds to cool down
Write-Verbose -Message ('Try to start {0}' -f $MonService)
$null = (Start-Sleep -Seconds 2)
try
{
# Now let us try to start the service
Write-Verbose -Message ('Try to start {0} again!' -f $MonService)
$null = (Start-Service @MonParam)
}
catch
{
# Dude, this is bad! And I mean real bad!!!
Write-Error -Message ('We where not able to start {0} - Might be a good idea to reboot this system' -f $MonService)
}
}
}
else
{
# Looks good so far
Write-Verbose -Message ('Looks like {0} is doing great...' -f $MonService)
}
}
#region LICENSE
<#
BSD 3-Clause License
Copyright (c) 2021, enabling Technology
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#>
#endregion LICENSE
#region DISCLAIMER
<#
DISCLAIMER:
- Use at your own risk, etc.
- This is open-source software, if you find an issue try to fix it yourself. There is no support and/or warranty in any kind
- This is a third-party Software
- The developer of this Software is NOT sponsored by or affiliated with Microsoft Corp (MSFT) or any of its subsidiaries in any way
- The Software is not supported by Microsoft Corp (MSFT)
- By using the Software, you agree to the License, Terms, and any Conditions declared and described above
- If you disagree with any of the terms, and any conditions declared: Just delete it and build your own solution
#>
#endregion DISCLAIMER

View File

@@ -0,0 +1,84 @@
function Clear-EnAllEventLogs
{
<#
.SYNOPSIS
AllEventLlogs
.DESCRIPTION
AllEventLlogs
.PARAMETER ComputerName
Computer Name
.EXAMPLE
PS C:\> Clear-EnAllEventLogs
.NOTES
N.N.
#>
[CmdletBinding(ConfirmImpact = 'Medium',
SupportsShouldProcess)]
param
(
[Parameter(ValueFromPipeline,
ValueFromPipelineByPropertyName)]
[string[]]
$ComputerName = "$env:COMPUTERNAME"
)
process
{
foreach ($SingleComputerName in $ComputerName)
{
if ($pscmdlet.ShouldProcess($SingleComputerName, 'Cleanup All EventLogs'))
{
$paramGetEventLog = @{
ComputerName = $SingleComputerName
List = $true
}
$null = (Get-EventLog @paramGetEventLog | ForEach-Object -Process {
if ($_.Entries)
{
$paramClearEventLog = @{
LogName = $_.Log
Confirm = $false
ErrorAction = 'SilentlyContinue'
}
$null = (Clear-EventLog @paramClearEventLog)
}
})
}
}
}
}
#region LICENSE
<#
BSD 3-Clause License
Copyright (c) 2021, enabling Technology
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#>
#endregion LICENSE
#region DISCLAIMER
<#
DISCLAIMER:
- Use at your own risk, etc.
- This is open-source software, if you find an issue try to fix it yourself. There is no support and/or warranty in any kind
- This is a third-party Software
- The developer of this Software is NOT sponsored by or affiliated with Microsoft Corp (MSFT) or any of its subsidiaries in any way
- The Software is not supported by Microsoft Corp (MSFT)
- By using the Software, you agree to the License, Terms, and any Conditions declared and described above
- If you disagree with any of the terms, and any conditions declared: Just delete it and build your own solution
#>
#endregion DISCLAIMER

View File

@@ -0,0 +1,373 @@
#requires -Version 4.0
<#
.SYNOPSIS
Compare a old and a refactored function to get any Performace differences
.DESCRIPTION
This script compares a simple function (That deletes all Windows Eventlog Entries) with an refacored one.
The request came up during a workshop: I was asked why I use pipes so much and if there is another way, without pipes.
The refactored version was created during the workshop as a prototype.
And to make it easier to compare them, I created this test script.
.EXAMPLE
PS C:\> .\Clear-EnAllEventLogs_TESTS.ps1
.NOTES
Releasenotes:
1.0.0 2019-07-24 Initial Version
THIS CODE IS MADE AVAILABLE AS IS, WITHOUT WARRANTY OF ANY KIND. THE ENTIRE RISK OF THE USE OR THE RESULTS FROM THE USE OF THIS CODE REMAINS WITH THE USER.
Dependencies:
NONE
.LINK
https://www.enatec.io
#>
[CmdletBinding(ConfirmImpact = 'None')]
[OutputType([psobject])]
param ()
#region VersionOfJosh
function Clear-EnAllEventLogs
{
<#
.SYNOPSIS
Delete all Windows event log entries
.DESCRIPTION
Delete all Windows event log entries, without any further interaction.
I use this only after I do some tests on a virtual machine.
Please Note:
It Might be dangerous! It might delete more than you like.
Warning:
All security related will also be removed completely.
If there were any issues, you might never find any information about it!
.PARAMETER ComputerName
Computer Name as String. Multi Value is possible
.EXAMPLE
PS C:\> Clear-EnAllEventLogs
Delete all Windows EventLog Entries on the local Computer.
.EXAMPLE
PS C:\> Clear-EnAllEventLogs -ComputerName FRADC01
Delete all Windows EventLog Entries on the Computer with the name FRADC01.
.EXAMPLE
PS C:\> Clear-EnAllEventLogs -ComputerName 'FRADC01', 'FRADC02'
Delete all Windows EventLog Entries on the Computers with the names FRADC01 and FRADC02.
.NOTES
Releasenotes:
THIS CODE IS MADE AVAILABLE AS IS, WITHOUT WARRANTY OF ANY KIND. THE ENTIRE RISK OF THE USE OR THE RESULTS FROM THE USE OF THIS CODE REMAINS WITH THE USER.
Dependencies:
TNONE
.LINK
https://www.enatec.io
.LINK
about_foreach
.LINK
Foreach-Object
.LINK
Get-EventLog
.LINK
Clear-EventLog
#>
[CmdletBinding(ConfirmImpact = 'Medium',
SupportsShouldProcess)]
param
(
[Parameter(ValueFromPipeline,
ValueFromPipelineByPropertyName)]
[string[]]
$ComputerName = "$env:COMPUTERNAME"
)
process
{
foreach ($SingleComputerName in $ComputerName)
{
if ($pscmdlet.ShouldProcess($SingleComputerName, 'Cleanup All EventLogs'))
{
$paramGetEventLog = @{
ComputerName = $SingleComputerName
List = $true
}
$null = (Get-EventLog @paramGetEventLog | ForEach-Object -Process {
if ($_.Entries)
{
$paramClearEventLog = @{
LogName = $_.Log
Confirm = $false
ErrorAction = 'SilentlyContinue'
}
$null = (Clear-EventLog @paramClearEventLog)
}
})
}
}
}
}
#endregion VersionOfJosh
#region RefactoredVersion
function Clear-EnAllEventLogsv2
{
<#
.SYNOPSIS
Delete all Windows event log entries
.DESCRIPTION
Delete all Windows event log entries, without any further interaction.
I use this only after I do some tests on a virtual machine.
Please Note:
It Might be dangerous! It might delete more than you like.
Warning:
All security related will also be removed completely.
If there were any issues, you might never find any information about it!
.PARAMETER ComputerName
Computer Name as String. Multi Value is possible
.EXAMPLE
PS C:\> Clear-EnAllEventLogsv2
Delete all Windows EventLog Entries on the local Computer.
.EXAMPLE
PS C:\> Clear-EnAllEventLogsv2 -ComputerName FRADC01
Delete all Windows EventLog Entries on the Computer with the name FRADC01.
.EXAMPLE
PS C:\> Clear-EnAllEventLogsv2 -ComputerName 'FRADC01', 'FRADC02'
Delete all Windows EventLog Entries on the Computers with the names FRADC01 and FRADC02.
.NOTES
Releasenotes:
2.0.0 2019-07-23: Refactored version
THIS CODE IS MADE AVAILABLE AS IS, WITHOUT WARRANTY OF ANY KIND. THE ENTIRE RISK OF THE USE OR THE RESULTS FROM THE USE OF THIS CODE REMAINS WITH THE USER.
Dependencies:
NONE
.LINK
https://www.enatec.io
.LINK
about_foreach
.LINK
Foreach-Object
.LINK
Get-EventLog
.LINK
Clear-EventLog
#>
[CmdletBinding(ConfirmImpact = 'Medium',
SupportsShouldProcess)]
param
(
[Parameter(ValueFromPipeline,
ValueFromPipelineByPropertyName)]
[string[]]
$ComputerName = "$env:COMPUTERNAME"
)
process
{
foreach ($SingleComputerName in $ComputerName)
{
if ($pscmdlet.ShouldProcess($SingleComputerName, 'Cleanup All EventLogs'))
{
$paramGetEventLog = @{
ComputerName = $SingleComputerName
List = $true
}
$null = ((Get-EventLog @paramGetEventLog).Where( {
if ($_.Entries)
{
$_
}
}).ForEach( {
$paramClearEventLog = @{
LogName = $_.Log
Confirm = $false
ErrorAction = 'SilentlyContinue'
}
$null = (Clear-EventLog @paramClearEventLog)
}))
}
}
}
}
#endregion RefactoredVersion
#region CreateTestData
function Invoke-CreateTestData
{
<#
.SYNOPSIS
Create 10.000 dummy entries
.DESCRIPTION
Create 10.000 dummy entries
.EXAMPLE
PS C:\> Invoke-CreateTestData
.NOTES
Internal Helper Function to create some useless Test Data
Releasenotes:
1.0.1 2019-07-23: Splat the parameters for better radability
1.0.0 2019-07-23: Initial Version
THIS CODE IS MADE AVAILABLE AS IS, WITHOUT WARRANTY OF ANY KIND. THE ENTIRE RISK OF THE USE OR THE RESULTS FROM THE USE OF THIS CODE REMAINS WITH THE USER.
Dependencies:
NONE
.LINK
https://www.enatec.io
.LINK
Write-EventLog
.LINK
about_foreach
.LINK
Foreach-Object
#>
[CmdletBinding(ConfirmImpact = 'None')]
param ()
begin
{
# Splat the parameters
$paramWriteEventLog = @{
LogName = 'Application'
EventId = 2001
EntryType = 'Information'
Source = 'HAL9000'
Message = 'I think you know what the problem is just as well as I do.'
ErrorAction = 'SilentlyContinue'
}
}
process
{
# Change the number to fit your needs
1 .. 1000 | ForEach-Object -Process {
$null = (Write-EventLog @paramWriteEventLog)
}
}
}
#endregion CreateTestData
# Initial Cleanup
$null = (Clear-EnAllEventLogs -ErrorAction SilentlyContinue)
# Create a few new objects
$OldWayAverage = @()
$OldWaySum = @()
$NewWayAverage = @()
$NewWaySum = @()
# Create the new Eventlog
$null = (New-EventLog -LogName Application -Source 'HAL9000' -ErrorAction SilentlyContinue)
#region OldWay
$null = (1..10 | ForEach-Object {
# Create some Test Data
$null = (Invoke-CreateTestData -ErrorAction SilentlyContinue)
#region OldWaySingle
$OldWaySingle = (Measure-Command -Expression {
$null = (Clear-EnAllEventLogs -ErrorAction SilentlyContinue)
})
#endregion OldWaySingle
$OldWaySum += $OldWaySingle
})
$OldWayAverage = (($OldWaySum | Measure-Object -Property TotalMilliseconds -Average).Average)
#endregion OldWay
#region NewWay
$null = (1..10 | ForEach-Object {
# Create some Test Data
$null = (Invoke-CreateTestData -ErrorAction SilentlyContinue)
#region NewWaySingle
$NewWaySingle = (Measure-Command -Expression {
$null = (Clear-EnAllEventLogsv2 -ErrorAction SilentlyContinue)
})
#endregion NewWaySingle
$NewWaySum += $NewWaySingle
})
$NewWayAverage = (($NewWaySum | Measure-Object -Property TotalMilliseconds -Average).Average)
#endregion NewWay
#Region DumpData
Write-Verbose -Message 'Time measured in milliseconds' -Verbose
[pscustomobject]@{
OldWay = $OldWayAverage
NewWay = $NewWayAverage
}
#endregion DumpData
#region LICENSE
<#
BSD 3-Clause License
Copyright (c) 2021, enabling Technology
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#>
#endregion LICENSE
#region DISCLAIMER
<#
DISCLAIMER:
- Use at your own risk, etc.
- This is open-source software, if you find an issue try to fix it yourself. There is no support and/or warranty in any kind
- This is a third-party Software
- The developer of this Software is NOT sponsored by or affiliated with Microsoft Corp (MSFT) or any of its subsidiaries in any way
- The Software is not supported by Microsoft Corp (MSFT)
- By using the Software, you agree to the License, Terms, and any Conditions declared and described above
- If you disagree with any of the terms, and any conditions declared: Just delete it and build your own solution
#>
#endregion DISCLAIMER

View File

@@ -0,0 +1,85 @@
function Clear-EnAllEventLogsv2
{
<#
.SYNOPSIS
AllEventLlogs
.DESCRIPTION
AllEventLlogs
.PARAMETER ComputerName
Computer Name
.EXAMPLE
PS C:\> Clear-EnAllEventLogsv2
.NOTES
N.N.
#>
[CmdletBinding(ConfirmImpact = 'Medium',
SupportsShouldProcess)]
param
(
[Parameter(ValueFromPipeline,
ValueFromPipelineByPropertyName)]
[string[]]
$ComputerName = "$env:COMPUTERNAME"
)
process
{
foreach ($SingleComputerName in $ComputerName)
{
if ($pscmdlet.ShouldProcess($SingleComputerName, 'Cleanup All EventLogs'))
{
$paramGetEventLog = @{
ComputerName = $SingleComputerName
List = $true
}
$null = ((Get-EventLog @paramGetEventLog).Where( {
if ($_.Entries)
{
$_
}
}).ForEach( {
$paramClearEventLog = @{
LogName = $_.Log
Confirm = $false
ErrorAction = 'SilentlyContinue'
}
$null = (Clear-EventLog @paramClearEventLog)
}))
}
}
}
}
#region LICENSE
<#
BSD 3-Clause License
Copyright (c) 2021, enabling Technology
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#>
#endregion LICENSE
#region DISCLAIMER
<#
DISCLAIMER:
- Use at your own risk, etc.
- This is open-source software, if you find an issue try to fix it yourself. There is no support and/or warranty in any kind
- This is a third-party Software
- The developer of this Software is NOT sponsored by or affiliated with Microsoft Corp (MSFT) or any of its subsidiaries in any way
- The Software is not supported by Microsoft Corp (MSFT)
- By using the Software, you agree to the License, Terms, and any Conditions declared and described above
- If you disagree with any of the terms, and any conditions declared: Just delete it and build your own solution
#>
#endregion DISCLAIMER

View File

@@ -0,0 +1,6 @@
# Disable the .NET Telemetry on production servers and critical workstations
[Environment]::SetEnvironmentVariable('DOTNET_CLI_TELEMETRY_OPTOUT', '1', 'Machine')
[Environment]::SetEnvironmentVariable('MLDOTNET_CLI_TELEMETRY_OPTOUT', '1', 'Machine')
# Tweak the 1st run experience
[Environment]::SetEnvironmentVariable('DOTNET_SKIP_FIRST_TIME_EXPERIENCE', '1', 'Machine')

View File

@@ -0,0 +1,272 @@
#requires -Version 3.0 -Modules CimCmdlets, DnsClient, NetAdapter, NetTCPIP -RunAsAdministrator
<#
.SYNOPSIS
Enable DNS-over-HTTPS (DoH) if device is not domain-joined
.DESCRIPTION
Enable DNS-over-HTTPS (DoH) if device is not domain-joined
It enables the Cloudflare DNS Servers, even if DoH is not working yet.
IPv6 Support is optional.
.PARAMETER IPv6
Enable IPv6 Support, IPv6 Servers will be added to the serverlist
.EXAMPLE
PS C:\> .\Enable-DNSOverHTTPS.ps1
Enable DNS-over-HTTPS (DoH) if device is not domain-joined for IPv4 only
.EXAMPLE
PS C:\> .\Enable-DNSOverHTTPS.ps1 -IPv6
Enable DNS-over-HTTPS (DoH) if device is not domain-joined for IPv4 and IPv6
.NOTES
Only the Insider Build of Windows 10 supports DoH!
But we configure it anyway!
The Cloudflare servers are used for regular DNS resolution and as soon as DoH is supported,
we can configure and use it anyway.
A future version of this script might support additional parameters, like DohFlags
You can also change the servers below to any service you like, e.g. Google DNS or Quad9 from IBM.
The Bool as return was requested by a customer, and the exit code (0 or 1) is implemented for our bootstrap setup
.LINK
https://1.1.1.1/dns/
.LINK
https://techcommunity.microsoft.com/t5/networking-blog/windows-insiders-can-now-test-dns-over-https/ba-p/1381282
#>
[CmdletBinding(ConfirmImpact = 'None')]
[OutputType([bool])]
param
(
[Parameter(ValueFromPipeline,
ValueFromPipelineByPropertyName)]
[Alias('IP6', '6')]
[switch]
$IPv6
)
begin
{
#region Defaults
$SCT = 'SilentlyContinue'
$STP = 'Stop'
$CNT = 'Continue'
# Save the infos from the switches
if (($PSCmdlet.MyInvocation.BoundParameters['Verbose']).IsPresent)
{
$IsVerbose = $true
}
else
{
$IsVerbose = $false
}
if (($PSCmdlet.MyInvocation.BoundParameters['Debug']).IsPresent)
{
$IsDebug = $true
}
else
{
$IsDebug = $false
}
if (($PSCmdlet.MyInvocation.BoundParameters['WhatIf']).IsPresent)
{
$IsWhatIf = $true
}
else
{
$IsWhatIf = $false
}
#endregion Defaults
#region ServerAddresses
# Create an Empty Object
$ServerAddresses = @()
# IPv4 DNS Servers to use
$ServerAddressesIPv4 = @(
'1.1.1.1'
'1.0.0.1'
)
# Add the IPv4 Servers to the Object
$ServerAddresses += $ServerAddressesIPv4
if ((($PSCmdlet.MyInvocation.BoundParameters['IPv6']).IsPresent) -eq $true)
{
Write-Verbose -Message 'IPv6 Servers will be added to the serverlist'
# IPv6 DNS Servers to use
$ServerAddressesIPv6 = @(
'2606:4700:4700::1111'
'2606:4700:4700::1001'
)
# Add the IPv6 Servers to the Object
$ServerAddresses += $ServerAddressesIPv6
}
#endregion ServerAddresses
}
process
{
#region DoH
# Enable DNS-over-HTTPS for IPv4 if device is not domain-joined
$paramGetCimInstance = @{
ClassName = 'CIM_ComputerSystem'
Verbose = $IsVerbose
Debug = $IsDebug
ErrorAction = $STP
}
if (((Get-CimInstance @paramGetCimInstance).PartOfDomain) -eq $false)
{
try
{
# Temporarily key
$paramNewItemProperty = @{
Path = 'HKLM:\SYSTEM\CurrentControlSet\Services\Dnscache\Parameters'
Name = 'EnableAutoDoh'
Value = 2
PropertyType = 'DWord'
Force = $true
WhatIf = $IsWhatIf
Verbose = $IsVerbose
Debug = $IsDebug
ErrorAction = $CNT
}
$null = (New-ItemProperty @paramNewItemProperty)
$paramGetNetAdapter = @{
Verbose = $IsVerbose
Debug = $IsDebug
Physical = $true
ErrorAction = $SCT
}
$MACAddress = ((Get-NetAdapter @paramGetNetAdapter).MacAddress)
$paramGetNetIPConfiguration = @{
Verbose = $IsVerbose
Debug = $IsDebug
ErrorAction = $SCT
}
$IpConfig = (Get-NetIPConfiguration @paramGetNetIPConfiguration | Where-Object -FilterScript {
$MACAddress -eq $_.NetAdapter.MacAddress
})
$paramSetDnsClientServerAddress = @{
ServerAddresses = $ServerAddresses
Verbose = $IsVerbose
Debug = $IsDebug
ErrorAction = $CNT
}
$null = ($IpConfig | Set-DnsClientServerAddress @paramSetDnsClientServerAddress)
$paramClearDnsClientCache = @{
Verbose = $IsVerbose
Debug = $IsDebug
ErrorAction = $SCT
}
$null = (Clear-DnsClientCache @paramClearDnsClientCache)
$paramRegisterDnsClient = @{
Verbose = $IsVerbose
Debug = $IsDebug
ErrorAction = $SCT
}
$null = (Register-DnsClient @paramRegisterDnsClient)
}
catch
{
# Get error record
[Management.Automation.ErrorRecord]$e = $_
# retrieve information about runtime error
$info = [PSCustomObject]@{
Exception = $e.Exception.Message
Reason = $e.CategoryInfo.Reason
Target = $e.CategoryInfo.TargetName
Script = $e.InvocationInfo.ScriptName
Line = $e.InvocationInfo.ScriptLineNumber
Column = $e.InvocationInfo.OffsetInLine
}
# output information. Post-process collected info, and log info (optional)
$info | Out-String | Write-Verbose
$paramWriteError = @{
Message = $e.Exception.Message
ErrorAction = $CNT
Exception = $e.Exception
TargetObject = $e.CategoryInfo.TargetName
}
Write-Error @paramWriteError
}
}
else
{
$paramWriteError = @{
Message = 'Sorry, this computer seems to be part of a Active Directory domain!'
Exception = 'Active Directory Domain Members are not supported'
Category = 'NotEnabled'
TargetObject = $env:COMPUTERNAME
ErrorAction = $CNT
}
Write-Error @paramWriteError
# Return the Bool
Write-Output -InputObject $false
# Unclean exit
exit 1
}
#endregion DoH
}
end
{
# Return the Bool
Write-Output -InputObject $true
# Clean exit
exit 0
}
#region LICENSE
<#
BSD 3-Clause License
Copyright (c) 2021, enabling Technology
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#>
#endregion LICENSE
#region DISCLAIMER
<#
DISCLAIMER:
- Use at your own risk, etc.
- This is open-source software, if you find an issue try to fix it yourself. There is no support and/or warranty in any kind
- This is a third-party Software
- The developer of this Software is NOT sponsored by or affiliated with Microsoft Corp (MSFT) or any of its subsidiaries in any way
- The Software is not supported by Microsoft Corp (MSFT)
- By using the Software, you agree to the License, Terms, and any Conditions declared and described above
- If you disagree with any of the terms, and any conditions declared: Just delete it and build your own solution
#>
#endregion DISCLAIMER

View File

@@ -0,0 +1,56 @@
#requires -RunAsAdministrator
<#
.SYNOPSIS
Force Time re-sync with PowerShell
.DESCRIPTION
Force Time re-sync as a PowerShell script
.EXAMPLE
PS C:\> .\ForceTimeResync.ps1
Force Time Resync as a PowerShell script (Wrapper for w32tm.exe). Most be executed in an elevated shell)
.NOTES
One of my VM's did a view time travels in the past. This little script runs every hour (Task).
I still try to find the cause for the time travels (It jumps 2 hours forward, from time to time) and a better PowerShell way to do it.
For now, this quick and dirty solution works just fine.
#>
[CmdletBinding(ConfirmImpact = 'None')]
param ()
process
{
$null = (& "$env:windir\system32\w32tm.exe" /resync /force)
}
#region LICENSE
<#
BSD 3-Clause License
Copyright (c) 2021, enabling Technology
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#>
#endregion LICENSE
#region DISCLAIMER
<#
DISCLAIMER:
- Use at your own risk, etc.
- This is open-source software, if you find an issue try to fix it yourself. There is no support and/or warranty in any kind
- This is a third-party Software
- The developer of this Software is NOT sponsored by or affiliated with Microsoft Corp (MSFT) or any of its subsidiaries in any way
- The Software is not supported by Microsoft Corp (MSFT)
- By using the Software, you agree to the License, Terms, and any Conditions declared and described above
- If you disagree with any of the terms, and any conditions declared: Just delete it and build your own solution
#>
#endregion DISCLAIMER

View File

@@ -0,0 +1,114 @@
function Get-AllCookiesFromWebRequestSession
{
<#
.SYNOPSIS
Get all cookies stored in the WebRequestSession variable from any Invoke-RestMethod and/or Invoke-WebRequest request
.DESCRIPTION
Get all cookies stored in the WebRequestSession variable from any Invoke-RestMethod and/or Invoke-WebRequest request
The WebRequestSession stores useful info and it has something that some my know as CookieJar or http.cookiejar.
.PARAMETER WebRequestSession
Specifies a variable where Invoke-RestMethod and/or Invoke-WebRequest saves values.
Must be a valid [Microsoft.PowerShell.Commands.WebRequestSession] object!
.EXAMPLE
PS C:\> $null = Invoke-WebRequest -UseBasicParsing -Uri 'http://jhochwald.com' -Method Get -SessionVariable WebSession -ErrorAction SilentlyContinue
PS C:\> $WebSession | Get-AllCookiesFromWebRequestSession
Get all cookies stored in the $WebSession variable from the request above.
This page doesn't use or set any cookies, but the (awesome) CloudFlare service does.
.EXAMPLE
$null = Invoke-RestMethod -UseBasicParsing -Uri 'https://jsonplaceholder.typicode.com/todos/1' -Method Get -SessionVariable RestSession -ErrorAction SilentlyContinue
$RestSession | Get-AllCookiesFromWebRequestSession
Get all cookies stored in the $RestSession variable from the request above.
Please do not abuse the free API service above!
.NOTES
I used something I had stolen from Chrissy LeMaire's TechNet Gallery entry a (very) long time ago.
But I needed something more generic, independent from the URL! This can become handy, to find any cookie from a 3rd party site or another host.
.LINK
https://docs.python.org/3/library/http.cookiejar.html
.LINK
https://en.wikipedia.org/wiki/HTTP_cookie
.LINK
https://gallery.technet.microsoft.com/scriptcenter/Getting-Cookies-using-3c373c7e
.LINK
Invoke-RestMethod
.LINK
Invoke-WebRequest
#>
[CmdletBinding(ConfirmImpact = 'None')]
param
(
[Parameter(Mandatory,
ValueFromPipeline,
ValueFromPipelineByPropertyName,
Position = 0,
HelpMessage = 'Specifies a variable where Invoke-RestMethod and/or Invoke-WebRequest saves values.')]
[ValidateNotNull()]
[Alias('Session', 'InputObject')]
[Microsoft.PowerShell.Commands.WebRequestSession]
$WebRequestSession
)
begin
{
# Do the housekeeping
$CookieInfoObject = $null
}
process
{
try
{
# I know, this look very crappy, but it just work fine!
[pscustomobject]$CookieInfoObject = ((($WebRequestSession).Cookies).GetType().InvokeMember('m_domainTable', [Reflection.BindingFlags]::NonPublic -bor [Reflection.BindingFlags]::GetField -bor [Reflection.BindingFlags]::Instance, $null, (($WebRequestSession).Cookies), @()))
}
catch
{
#region ErrorHandler
# get error record
[Management.Automation.ErrorRecord]$e = $_
# retrieve information about runtime error
$info = [PSCustomObject]@{
Exception = $e.Exception.Message
Reason = $e.CategoryInfo.Reason
Target = $e.CategoryInfo.TargetName
Script = $e.InvocationInfo.ScriptName
Line = $e.InvocationInfo.ScriptLineNumber
Column = $e.InvocationInfo.OffsetInLine
}
# output information. Post-process collected info, and log info (optional)
$info | Out-String | Write-Verbose
$paramWriteError = @{
Message = $e.Exception.Message
ErrorAction = 'Stop'
Exception = $e.Exception
TargetObject = $e.CategoryInfo.TargetName
}
Write-Error @paramWriteError
# Only here to catch a global ErrorAction overwrite
exit 1
#endregion ErrorHandler
}
}
end
{
# Dump the Cookies to the Console
((($CookieInfoObject).Values).Values)
}
}

View File

@@ -0,0 +1,134 @@
function Get-DirectorySize
{
<#
.SYNOPSIS
Get the size of a given folder in a human readable format
.DESCRIPTION
Get the size of a given folder in a human readable format
.PARAMETER Path
Folder to check
.PARAMETER Type
Type of the Return,
Valid values are: GB, MB, KB, B
The default is MB (Megabyte)
.EXAMPLE
PS C:\> Get-DirectorySize -Path 'C:\scripts'
.EXAMPLE
PS C:\> Get-DirectorySize -Path 'C:\scripts' -Type GB
.NOTES
PowerShell function to emulate the wel known Linux DU command
Releasenotes:
1.0.0 2019-05-09: Initial Release
THIS CODE IS MADE AVAILABLE AS IS, WITHOUT WARRANTY OF ANY KIND. THE ENTIRE RISK OF THE USE OR THE RESULTS FROM THE USE OF THIS CODE REMAINS WITH THE USER.
#>
[CmdletBinding(ConfirmImpact = 'None')]
[OutputType([string])]
param
(
[Parameter(ValueFromPipeline,
ValueFromPipelineByPropertyName)]
[ValidateNotNullOrEmpty()]
[Alias('Directory', 'Folder')]
[string]
$Path = '.',
[Parameter(ValueFromPipeline,
ValueFromPipelineByPropertyName)]
[ValidateNotNullOrEmpty()]
[ValidateSet('GB', 'MB', 'KB', 'B', IgnoreCase = $true)]
[Alias('InType')]
[string]
$Type = 'MB'
)
process
{
try
{
$AllFolderItems = (Get-ChildItem -Path $Path -Recurse -ErrorAction Stop | Measure-Object -Property length -Sum)
switch ($Type)
{
'GB'
{
$FolderSize = '{0:N2}' -f ($AllFolderItems.sum / 1GB) + ' GB'
}
'MB'
{
$FolderSize = '{0:N2}' -f ($AllFolderItems.sum / 1MB) + ' MB'
}
'KB'
{
$FolderSize = '{0:N2}' -f ($AllFolderItems.sum / 1KB) + ' KB'
}
'B'
{
$FolderSize = '{0:N2}' -f ($AllFolderItems.sum) + ' B'
}
Default
{
$FolderSize = '{0:N2}' -f ($AllFolderItems.sum) + ' MB'
}
}
return $FolderSize
}
catch
{
# get error record
[Management.Automation.ErrorRecord]$e = $_
# retrieve information about runtime error
$info = [PSCustomObject]@{
Exception = $e.Exception.Message
Reason = $e.CategoryInfo.Reason
Target = $e.CategoryInfo.TargetName
Script = $e.InvocationInfo.ScriptName
Line = $e.InvocationInfo.ScriptLineNumber
Column = $e.InvocationInfo.OffsetInLine
}
# output information. Post-process collected info, and log info (optional)
$info | Out-String | Write-Verbose
Write-Error -Message $e.Exception.Message -ErrorAction Continue -Exception $e.Exception -TargetObject $e.CategoryInfo.TargetName
}
}
}
#region LICENSE
<#
BSD 3-Clause License
Copyright (c) 2021, enabling Technology
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#>
#endregion LICENSE
#region DISCLAIMER
<#
DISCLAIMER:
- Use at your own risk, etc.
- This is open-source software, if you find an issue try to fix it yourself. There is no support and/or warranty in any kind
- This is a third-party Software
- The developer of this Software is NOT sponsored by or affiliated with Microsoft Corp (MSFT) or any of its subsidiaries in any way
- The Software is not supported by Microsoft Corp (MSFT)
- By using the Software, you agree to the License, Terms, and any Conditions declared and described above
- If you disagree with any of the terms, and any conditions declared: Just delete it and build your own solution
#>
#endregion DISCLAIMER

View File

@@ -0,0 +1,463 @@
function Get-FritzBoxEvents
{
<#
.SYNOPSIS
Get the Events from a FritzBox router
.DESCRIPTION
Get the Events from a FritzBox router
.PARAMETER FritzBoxUser
Username to use for the FritzBox login
.PARAMETER FritzBoxPassword
FritzBox Password in plain text (might be changed to a secure string soon)
.PARAMETER FritzBoxHost
The URI that contains the FQDN or IP of your FritzBox,
e.g. http://fritz.box or http://192.168.178.1
.PARAMETER Hours
Hours to get, e.g. 24
.EXAMPLE
PS C:\> Get-FritzBoxEvents -FritzBoxUser 'myFritz' -FritzBoxPassword 'ThePassw0rd' -FritzBoxHost 'http://myfritz.box' -Hours 24 | Where-Object {(($_.ipv4 -ne $null) -or ($_.ipv6 -ne $null))}
Get only entries with IPv4 or IPv6 values, of the last 24 hours
.EXAMPLE
PS C:\> Get-FritzBoxEvents -FritzBoxUser 'myFritz' -FritzBoxPassword 'ThePassw0rd' -FritzBoxHost 'http://myfritz.box' -Hours 48 | Where-Object {($_.ipv6 -ne $null)}
Get only entries with IPv6 values, of the last 48 hours
.EXAMPLE
PS C:\> Get-FritzBoxEvents -FritzBoxUser 'myFritz' -FritzBoxPassword 'ThePassw0rd' -FritzBoxHost 'http://myfritz.box' | Where-Object {($_.Message -like 'Die Systemzeit wurde erfolgreich aktualisiert von Zeitserver*')}
Get all events where the time was set via a time server, no time limit
.EXAMPLE
PS C:\> Get-FritzBoxEvents -FritzBoxUser 'myFritz' -FritzBoxPassword 'ThePassw0rd' -FritzBoxHost 'http://myfritz.box' | Where-Object {($_.Message -like 'Die Systemzeit wurde erfolgreich aktualisiert von Zeitserver*')} | Select-Object -ExpandProperty IPv4
Get all events where the time was set via a time server, only return the IPv4 addresses, no time limit
.EXAMPLE
PS C:\> ((Get-FritzBoxEvents -FritzBoxUser 'myFritz' -FritzBoxPassword 'ThePassw0rd' -FritzBoxHost 'http://myfritz.box' | Where-Object {($_.Message -like 'Die Systemzeit wurde erfolgreich aktualisiert von Zeitserver*')})[0] | Select-Object -ExpandProperty IPv4)
Get all events where the time was set via a time server, only return the IPv4 address of the latest (youngest) event
.EXAMPLE
PS C:\> Get-FritzBoxEvents -FritzBoxUser 'myFritz' -FritzBoxPassword 'ThePassw0rd' -FritzBoxHost 'http://myfritz.box' | Where-Object {($_.Message -like '*main-repeater*')}
Only return events from a repeater with the name main-repeater, no time limit
.EXAMPLE
PS C:\> (Get-FritzBoxEvents -FritzBoxUser 'myFritz' -FritzBoxPassword 'ThePassw0rd' -FritzBoxHost 'http://myfritz.box' | Where-Object {($_.Message -like '*main-repeater*')})[0]
Only return the latest (youngest) events from a repeater with the name main-repeater, no time limit
.EXAMPLE
PS C:\> Get-FritzBoxEvents -FritzBoxUser 'myFritz' -FritzBoxPassword 'ThePassw0rd' -FritzBoxHost 'http://myfritz.box' | Where-Object {($_.Message -like '*Zeitserver * antwortet nicht.')}
Only return events where the Timeserver does NOT answer, no time limit
.EXAMPLE
PS C:\> Get-FritzBoxEvents -FritzBoxUser 'myFritz' -FritzBoxPassword 'ThePassw0rd' -FritzBoxHost 'http://myfritz.box' | Where-Object {($_.Message -like '*verschlüsselten DNS-Servern*')}
All events related to encrypted DNS, no time limit
.EXAMPLE
PS C:\> Get-FritzBoxEvents -FritzBoxUser 'myFritz' -FritzBoxPassword 'ThePassw0rd' -FritzBoxHost 'http://myfritz.box' -Hours 24 | Where-Object {($_.Message -like '*Authentifizierungsfehler*')}
Only events with authentication errors, of the last 24 hours
.EXAMPLE
PS C:\> ((Get-FritzBoxEvents -FritzBoxUser 'myFritz' -FritzBoxPassword 'ThePassw0rd' -FritzBoxHost 'http://myfritz.box' | Where-Object {($_.Message -like '*(verfügbare Bitrate)*')})[0] | Select-Object -ExpandProperty Message)
The latest (youngest) event that has the bitrate info (capacity)
.EXAMPLE
PS C:\> ((Get-FritzBoxEvents -FritzBoxUser 'myFritz' -FritzBoxPassword 'ThePassw0rd' -FritzBoxHost 'http://myfritz.box' | Where-Object {($_.Message -like 'Internetverbindung wurde erfolgreich hergestellt.*')})[0] | Select-Object -ExpandProperty IPv4)
Get the public IPv4 address
.EXAMPLE
PS C:\> ((Get-FritzBoxEvents -FritzBoxUser 'myFritz' -FritzBoxPassword 'ThePassw0rd' -FritzBoxHost 'http://myfritz.box' | Where-Object {($_.Message -like 'Internetverbindung IPv6 wurde erfolgreich hergestellt.*')})[0] | Select-Object -ExpandProperty IPv6)
Get the public IPv6 address
.EXAMPLE
PS C:\> ((Get-FritzBoxEvents -FritzBoxUser 'myFritz' -FritzBoxPassword 'ThePassw0rd' -FritzBoxHost 'http://myfritz.box' | Where-Object {(($_.Message -like '*IPv6-Präfix wurde erfolgreich bezogen.*') -and ($_.ipv6 -ne $null))})[0] | Select-Object -ExpandProperty IPv6)
Get the latest public IPv6 prefix (CIDR)
.EXAMPLE
PS C:\> ((Get-FritzBoxEvents -FritzBoxUser 'myFritz' -FritzBoxPassword 'ThePassw0rd' -FritzBoxHost 'http://myfritz.box' | Where-Object {(($_.Message -like 'Freigabe als Exposed Host auf * (*) hinzugefügt.') -and ($_.ipv4 -ne $null))})[0] | Select-Object -ExpandProperty IPv4)
get the exposed host IPv4
.EXAMPLE
PS C:\> $IPv6TMP = (Get-FritzBoxEvents -FritzBoxUser 'myFritz' -FritzBoxPassword 'ThePassw0rd' -FritzBoxHost 'http://myfritz.box' | Where-Object {(($_.Message -like 'Freigabe als Exposed Host auf * (*) hinzugefügt.') -and ($_.ipv4 -eq $null))} | Select-Object -ExpandProperty Message)
PS C:\> $regex = [regex]'(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))'
PS C:\> $regex.Matches($IPv6TMP) | ForEach-Object{ $_.value }
Get the exposed host IPv6 address and/or IPv6 CIDR (of exists)
.LINK
https://github.com/jangeisbauer/FritzBox2Sentinel
.LINK
https://gist.github.com/joasch/e48738417ec1efcc963a96bbb3f34cba
.LINK
https://www.ip-phone-forum.de/threads/ereignisprotokoll-der-fritz-box-auf-linux-server-sichern.280328/page-5
.NOTES
All tests in the examples are only valid if your FritzBox has a german UI!
For other languages, dump all events and search for the matches in your own language
If you have issues with german umlauts, use the following before stating the command:
[console]::OutputEncoding = [System.Text.Encoding]::GetEncoding(1252)
I had issues on macOS and Linux with german umlauts, never happened on Windows!
Idea is stolen from https://github.com/jangeisbauer/FritzBox2Sentinel (No license was applied)
So, @jangeisbauer is considered as a contributor
#>
[CmdletBinding(ConfirmImpact = 'None')]
[OutputType([array])]
param
(
[Parameter(ValueFromPipeline,
ValueFromPipelineByPropertyName)]
[ValidateNotNull()]
[ValidateNotNullOrEmpty()]
[Alias('FBUser', 'user')]
[string]
$FritzBoxUser = $null,
[Parameter(ValueFromPipeline,
ValueFromPipelineByPropertyName)]
[ValidateNotNull()]
[ValidateNotNullOrEmpty()]
[Alias('Password', 'fbpassword')]
[string]
$FritzBoxPassword = $null,
[Parameter(ValueFromPipeline,
ValueFromPipelineByPropertyName)]
[ValidateNotNull()]
[ValidateNotNullOrEmpty()]
[Alias('fbhost', 'host', 'fritzbox')]
[string]
$FritzBoxHost = 'http://fritz.box',
[Parameter(ValueFromPipeline,
ValueFromPipelineByPropertyName)]
[int]
$Hours = $null
)
begin
{
# Garbage Collection
[GC]::Collect()
#region Helper
function Get-MD5Hash
{
<#
.SYNOPSIS
Return a MD5 hash of a given String
.DESCRIPTION
Return a MD5 hash of a given String
.PARAMETER Text
String to convert
.EXAMPLE
PS C:\> Get-MD5Hash -Text 'Value1'
.LINK
https://github.com/jangeisbauer/FritzBox2Sentinel
.NOTES
Cheap internal helper
Stolen from https://github.com/jangeisbauer/FritzBox2Sentinel (No license was applied)
#>
[CmdletBinding(ConfirmImpact = 'None')]
[OutputType([string])]
param
(
[Parameter(Mandatory,
ValueFromPipeline,
ValueFromPipelineByPropertyName,
HelpMessage = 'String to convert')]
[ValidateNotNull()]
[ValidateNotNullOrEmpty()]
[string]
$Text
)
begin
{
$md5 = (New-Object -TypeName System.Security.Cryptography.MD5CryptoServiceProvider)
}
process
{
$md5.ComputeHash([Text.Encoding]::utf8.getbytes($Text)) | ForEach-Object -Process {
$HC = ''
} {
$HC += $_.tostring('x2')
} {
$HC
}
}
}
#endregion Helper
}
process
{
try
{
# Convert the plain text password to a secure string
$FritzBoxSecurePassword = ($FritzBoxPassword | ConvertTo-SecureString -AsPlainText -Force -ErrorAction Stop)
# FritzBox Pages to get
$FritzBoxLoginPage = '/login_sid.lua'
$FritzBoxEventPage = '/query.lua?mq_log=logger:status/log&sid='
# Secret handler
$SecureStringToBSTR = [Runtime.InteropServices.Marshal]::SecureStringToBSTR($FritzBoxSecurePassword)
$PtrToStringAuto = [Runtime.InteropServices.Marshal]::PtrToStringBSTR($SecureStringToBSTR)
# Get the challenge from the FritzBox Login Page
$ChallengeRequest = (Invoke-WebRequest -Uri ($FritzBoxHost + $FritzBoxLoginPage) -UseBasicParsing -ErrorAction Stop)
# Save the recived challenge
$Challenge = ([xml]$ChallengeRequest).sessioninfo.challenge
# Create the input for the HEX code
$Code1 = ($Challenge + '-' + $PtrToStringAuto)
# Create the HEX data string
$Code2 = ([char[]]$Code1 | ForEach-Object -Process {
$Code2 = ''
} {
$Code2 += $_ + [Char]0
} {
$Code2
})
# Create the body part for the next request (includes the MD5 hash of the HEX from above)
$SIDRequestBody = ('response=' + $Challenge + '-' + $(Get-MD5Hash -text ($Code2)) + '&username=' + $FritzBoxUser)
# Do the real Login
$SIDRequest = (Invoke-WebRequest -Uri ($FritzBoxHost + $FritzBoxLoginPage) -Method Post -Body $SIDRequestBody -ErrorAction Stop)
# Extract the SID from the Login request
$SID = ((([xml]($SIDRequest.Content)).ChildNodes).sid)
# Get the Events
$FritzBoxEvents = (Invoke-WebRequest -Uri ($FritzBoxHost + $FritzBoxEventPage + $SID) -UseBasicParsing -ErrorAction Stop)
# Do we have a time limit?
if ($Hours -ne 0)
{
# Create a filter
$Filterhours = ((Get-Date).AddHours(-$Hours))
}
else
{
# No filter needed
$Filterhours = $null
}
# Create a new Array
$FritzEvents = @()
# loop over the events we have (and extract the JSON return that contains all events)
foreach ($FritzBoxEvent in ($FritzBoxEvents.Content | ConvertFrom-Json -ErrorAction Stop).mq_log)
{
try
{
# Cleanup
$EventDate = $null
$IPv6 = $null
$IPv4 = $null
$EventEntry = $null
# Transform the Data
$EventDate = [regex]::Matches($FritzBoxEvent, '\d\d\.\d\d\.\d\d \d\d:\d\d:\d\d')[0].Value
# This REGEX should match IPv6 and IPv6 CIDR
$IPv6 = [regex]::Matches($FritzBoxEvent[0], '((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:)))(%.+)?s*(\/([0-9]|[1-9][0-9]|1[0-1][0-9]|12[0-8]))?$')[0].Value
# Simple IPv4 REGEX
$IPv4 = [regex]::Matches($FritzBoxEvent[0], '(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)')[0].Value
# Do we have a DATE in the event?
if ($EventDate -ne '')
{
# Transform the Event DATE
$EventEntry = $FritzBoxEvent[0].replace($EventDate, '')
# Ensure we have the correct format, just in case
#$EventDate = (Get-Date -Date $EventDate)
}
# Apply the Limit, if needed
if (($Filterhours) -and ($EventDate -ge $Filterhours))
{
# Cleanup the event message (remove leading or trailing whitespaces)
$EventEntry = $EventEntry.trim()
# Add the Event to the list
$FritzEvents += [PSCustomObject]@{
Date = $EventDate
Message = $EventEntry
IPv4 = $IPv4
IPv6 = $IPv6
}
}
# Cleanup
$EventDate = $null
$IPv6 = $null
$IPv4 = $null
$EventEntry = $null
}
catch
{
#region ErrorHandler
# get error record
[Management.Automation.ErrorRecord]$e = $_
# retrieve information about runtime error
$info = [PSCustomObject]@{
Exception = $e.Exception.Message
Reason = $e.CategoryInfo.Reason
Target = $e.CategoryInfo.TargetName
Script = $e.InvocationInfo.ScriptName
Line = $e.InvocationInfo.ScriptLineNumber
Column = $e.InvocationInfo.OffsetInLine
}
# output information. Post-process collected info, and log info (optional)
$info | Out-String | Write-Verbose
Write-Warning -Message $e.Exception.Message -WarningAction Continue
#endregion ErrorHandler
}
}
}
catch
{
# Cleanup
$FritzEvents = $null
$FritzBoxSecurePassword = $null
$FritzBoxLoginPage = $null
$FritzBoxEventPage = $null
$SecureStringToBSTR = $null
$PtrToStringAuto = $null
$ChallengeRequest = $null
$Challenge = $null
$Code1 = $null
$Code2 = $null
$SIDRequestBody = $null
$SIDRequest = $null
$SID = $null
$FritzBoxEvents = $null
$Hours = $null
$Filterhours = $null
#region ErrorHandler
# get error record
[Management.Automation.ErrorRecord]$e = $_
# retrieve information about runtime error
$info = [PSCustomObject]@{
Exception = $e.Exception.Message
Reason = $e.CategoryInfo.Reason
Target = $e.CategoryInfo.TargetName
Script = $e.InvocationInfo.ScriptName
Line = $e.InvocationInfo.ScriptLineNumber
Column = $e.InvocationInfo.OffsetInLine
}
# output information. Post-process collected info, and log info (optional)
$info | Out-String | Write-Verbose
$paramWriteError = @{
Message = $e.Exception.Message
ErrorAction = 'Stop'
Exception = $e.Exception
TargetObject = $e.CategoryInfo.TargetName
}
Write-Error @paramWriteError
# Only here to catch a global ErrorAction overwrite
exit 1
#endregion ErrorHandler
}
finally
{
# Garbage Collection
[GC]::Collect()
}
}
end
{
# Dump to the Terminal
$FritzEvents
# Cleanup
$FritzEvents = $null
$FritzBoxSecurePassword = $null
$FritzBoxLoginPage = $null
$FritzBoxEventPage = $null
$SecureStringToBSTR = $null
$PtrToStringAuto = $null
$ChallengeRequest = $null
$Challenge = $null
$Code1 = $null
$Code2 = $null
$SIDRequestBody = $null
$SIDRequest = $null
$SID = $null
$FritzBoxEvents = $null
$Hours = $null
$Filterhours = $null
# Garbage Collection
[GC]::Collect()
}
}
#region LICENSE
<#
BSD 3-Clause License
Copyright (c) 2021, enabling Technology
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#>
#endregion LICENSE
#region DISCLAIMER
<#
DISCLAIMER:
- Use at your own risk, etc.
- This is open-source software, if you find an issue try to fix it yourself. There is no support and/or warranty in any kind
- This is a third-party Software
- The developer of this Software is NOT sponsored by or affiliated with Microsoft Corp (MSFT) or any of its subsidiaries in any way
- The Software is not supported by Microsoft Corp (MSFT)
- By using the Software, you agree to the License, Terms, and any Conditions declared and described above
- If you disagree with any of the terms, and any conditions declared: Just delete it and build your own solution
#>
#endregion DISCLAIMER

View File

@@ -0,0 +1,188 @@
function Get-IPv6InWindows
{
<#
.SYNOPSIS
Get the configured IPv6 value from the registry
.DESCRIPTION
Get the configured IPv6 value from the registry
Transforms the Registry value into human understandable values
.EXAMPLE
PS C:\> Get-IPv6InWindows
All IPv6 components are enabled (0)
.EXAMPLE
PS C:\> Get-IPv6InWindows -verbose
Prefer IPv4 over IPv6 (32)
Get the configured IPv6 value from the registry, with verbose output
.LINK
Set-IPv6InWindows
.LINK
https://docs.microsoft.com/en-us/troubleshoot/windows-server/networking/configure-ipv6-in-windows
.LINK
https://docs.microsoft.com/en-us/troubleshoot/windows-server/networking/configure-ipv6-in-windows#reference
.NOTES
Just a wrapper to make the values more human readable.
This is just a quick and dirty initial version!
If you find any further values (other then the supported), please let me know!
Want to modify your IPv6 configuration? Use its companion Set-IPv6InWindows
#>
[CmdletBinding(ConfirmImpact = 'None')]
[OutputType([string])]
param ()
begin
{
# Cleanup
$ComponentValue = $null
$ComponentValueText = $null
#region BoundParameters
if (($PSCmdlet.MyInvocation.BoundParameters['Verbose']).IsPresent)
{
$IsVerbose = $true
}
else
{
$IsVerbose = $false
}
if (($PSCmdlet.MyInvocation.BoundParameters['Debug']).IsPresent)
{
$IsDebug = $true
}
else
{
$IsDebug = $false
}
#endregion BoundParameters
}
process
{
# Get the Value from the registry
try
{
$paramGetItemProperty = @{
Path = 'HKLM:\SYSTEM\CurrentControlSet\Services\tcpip6\Parameters'
Name = 'DisabledComponents'
Debug = $IsDebug
Verbose = $IsVerbose
ErrorAction = 'Stop'
WarningAction = 'Continue'
}
$ComponentValue = (Get-ItemProperty @paramGetItemProperty | Select-Object -ExpandProperty DisabledComponents -ErrorAction Stop -WarningAction Continue)
}
catch
{
#region ErrorHandler
# get error record
[Management.Automation.ErrorRecord]$e = $_
# retrieve information about runtime error
$info = [PSCustomObject]@{
Exception = $e.Exception.Message
Reason = $e.CategoryInfo.Reason
Target = $e.CategoryInfo.TargetName
Script = $e.InvocationInfo.ScriptName
Line = $e.InvocationInfo.ScriptLineNumber
Column = $e.InvocationInfo.OffsetInLine
}
Write-Verbose -Message $info
Write-Error -Message ($info.Exception) -ErrorAction Stop
# Only here to catch a global ErrorAction overwrite
exit 1
#endregion ErrorHandler
}
switch ($ComponentValue)
{
0
{
$ComponentValueText = ('All IPv6 components are enabled ({0})' -f $ComponentValue)
}
255
{
$ComponentValueText = ('All IPv6 components are disabled ({0})' -f $ComponentValue)
}
2
{
$ComponentValueText = ('6to4 is disabled ({0})' -f $ComponentValue)
}
4
{
$ComponentValueText = ('ISATAP is disabled ({0})' -f $ComponentValue)
}
8
{
$ComponentValueText = ('Teredo is disabled ({0})' -f $ComponentValue)
}
10
{
$ComponentValueText = ('Teredo and 6to4 is disabled ({0})' -f $ComponentValue)
}
1
{
$ComponentValueText = ('All tunnel interfaces are disabled ({0})' -f $ComponentValue)
}
16
{
$ComponentValueText = ('All LAN and PPP interfaces are disabled ({0})' -f $ComponentValue)
}
17
{
$ComponentValueText = ('All LAN, PPP and tunnel interfaces are disabled ({0})' -f $ComponentValue)
}
32
{
$ComponentValueText = ('Prefer IPv4 over IPv6 ({0})' -f $ComponentValue)
}
default
{
$ComponentValueText = ('Unknown value found: {0}' -f $ComponentValue)
}
}
}
end
{
# Dump the info
$ComponentValueText
}
}
#region LICENSE
<#
BSD 3-Clause License
Copyright (c) 2021, enabling Technology
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#>
#endregion LICENSE
#region DISCLAIMER
<#
DISCLAIMER:
- Use at your own risk, etc.
- This is open-source software, if you find an issue try to fix it yourself. There is no support and/or warranty in any kind
- This is a third-party Software
- The developer of this Software is NOT sponsored by or affiliated with Microsoft Corp (MSFT) or any of its subsidiaries in any way
- The Software is not supported by Microsoft Corp (MSFT)
- By using the Software, you agree to the License, Terms, and any Conditions declared and described above
- If you disagree with any of the terms, and any conditions declared: Just delete it and build your own solution
#>
#endregion DISCLAIMER

View File

@@ -0,0 +1,75 @@
<#
.SYNOPSIS
Get all local IP addresses
.DESCRIPTION
Get all local IP addresses, just the addresses
.EXAMPLE
PS C:\> .\Get-IpInfo.ps1
Get all local IP addresses, just the addresses
.NOTES
Quick an dirty function that uses Net.DNS to gather the information about the IP Addresses
#>
[CmdletBinding(ConfirmImpact = 'None')]
[OutputType([psobject])]
param ()
begin
{
#Cleanup
$IpAddressInfo = $null
}
process
{
# Get the Info using Net.Dns
$IpAddressInfo = @(
(([Net.DNS]::GetHostAddresses([Net.Dns]::GetHostByName(($env:COMPUTERNAME)).HostName) | Where-Object -FilterScript {
$_.IsIPv6LinkLocal -eq $false
}).IPAddressToString | Where-Object -FilterScript {
$_ -ne '::1'
})
)
}
end
{
# Dump the Info
$IpAddressInfo
#Cleanup
$IpAddressInfo = $null
}
#region LICENSE
<#
BSD 3-Clause License
Copyright (c) 2021, enabling Technology
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#>
#endregion LICENSE
#region DISCLAIMER
<#
DISCLAIMER:
- Use at your own risk, etc.
- This is open-source software, if you find an issue try to fix it yourself. There is no support and/or warranty in any kind
- This is a third-party Software
- The developer of this Software is NOT sponsored by or affiliated with Microsoft Corp (MSFT) or any of its subsidiaries in any way
- The Software is not supported by Microsoft Corp (MSFT)
- By using the Software, you agree to the License, Terms, and any Conditions declared and described above
- If you disagree with any of the terms, and any conditions declared: Just delete it and build your own solution
#>
#endregion DISCLAIMER

View File

@@ -0,0 +1,106 @@
function Get-LocalGroupMembership
{
<#
.SYNOPSIS
Get all local Groups a given User is a Member of
.DESCRIPTION
The the the membership of all local Groups for a given User.
The Given User could be a local User (COMPUTER\USER) or a Domain User (DOMAIN\USER).
.PARAMETER UserName
Given User could be a local User (COMPUTER\USER) or a Domain User (DOMAIN\USER).
Default is the user that executes the function.
.EXAMPLE
PS C:\> Get-LocalGroupMembership
Dump the Group Membership for the User that executes the function
.EXAMPLE
PS C:\> Get-LocalGroupMembership -UserName 'CONTOSO\John.Doe'
Dump the Group Membership for the User John.Doe in the Domain CONTOSO
.EXAMPLE
PS C:\> Get-LocalGroupMembership -UserName "$env:COMPUTERNAME\John.Doe"
Dump the Group Membership for the User John.Doe on the local computer
.EXAMPLE
PS C:\> Get-LocalGroupMembership -UserName 'CONTOSO\John.Doe' | Foreach-Object { Add-LocalGroupMember -Group $_ -Member "$env:COMPUTERNAME\John.Doe" -ErrorAction SilentlyContinue }
Clone the Group Membership from User John.Doe in the Domain CONTOSO to User John.Doe on the local computer
.NOTES
This is just a quick and dirty solution for a problem I faced. (See last example)
#>
[CmdletBinding(ConfirmImpact = 'None')]
[OutputType([psobject])]
param
(
[Parameter(ValueFromPipeline,
ValueFromPipelineByPropertyName)]
[ValidateNotNullOrEmpty()]
[Alias('User')]
[string]
$UserName = ("$env:USERDOMAIN" + '\' + "$env:USERNAME")
)
begin
{
# Create a new Object
$LocalGroupMembership = @()
}
process
{
$AllGroups = (Get-LocalGroup -Name *)
foreach ($LocalGroup in $AllGroups)
{
if (Get-LocalGroupMember -Group $LocalGroup.Name -ErrorAction SilentlyContinue | Where-Object -FilterScript {
$_.name -eq $UserName
})
{
$LocalGroupMembership += $LocalGroup.Name
}
}
}
end
{
# Dump the object to the console
$LocalGroupMembership
}
}
#region LICENSE
<#
BSD 3-Clause License
Copyright (c) 2021, enabling Technology
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#>
#endregion LICENSE
#region DISCLAIMER
<#
DISCLAIMER:
- Use at your own risk, etc.
- This is open-source software, if you find an issue try to fix it yourself. There is no support and/or warranty in any kind
- This is a third-party Software
- The developer of this Software is NOT sponsored by or affiliated with Microsoft Corp (MSFT) or any of its subsidiaries in any way
- The Software is not supported by Microsoft Corp (MSFT)
- By using the Software, you agree to the License, Terms, and any Conditions declared and described above
- If you disagree with any of the terms, and any conditions declared: Just delete it and build your own solution
#>
#endregion DISCLAIMER

View File

@@ -0,0 +1,95 @@
function Get-LocalIpAddresses
{
<#
.SYNOPSIS
Print a string with all IP addresses
.DESCRIPTION
Print a string with all IP addresses. Supports IPv4 and IPv6.
It filters IPv6 Link Local only addresses by default.
.PARAMETER TargetName
Specifies the computers to test. Type the computer names or type IP addresses in IPv4 or IPv6 format. Wildcard characters are not permitted. The default is localhost.
.PARAMETER IPv6LinkLocal
Retuns IPv6 Link Local only addresses? Off by default.
.EXAMPLE
PS C:\> Get-LocalIpAddresses
Print a string with all local IP addresses
.EXAMPLE
PS C:\> Get-LocalIpAddresses -TargetName 'mycomputer'
Print a string with all IP addresses for the computer 'mycomputer'
.NOTES
TODO: Remove the -TargetName in the next release! Makes no sense (only IPv4 is returned)
#>
[CmdletBinding(ConfirmImpact = 'None')]
[OutputType([string])]
param
(
[Parameter(ValueFromPipeline,
ValueFromPipelineByPropertyName,
Position = 1)]
[ValidateNotNullOrEmpty()]
[string]
$TargetName = $env:COMPUTERNAME,
[Parameter(ValueFromPipeline,
ValueFromPipelineByPropertyName,
Position = 2)]
[Alias('IsIPv6LinkLocal')]
[switch]
$IPv6LinkLocal
)
begin
{
$IpInfo = $null
}
process
{
$IpInfo = ($TargetName | ForEach-Object -Process {
(([Net.DNS]::GetHostAddresses([Net.Dns]::GetHostByName($_).HostName) | Where-Object -FilterScript {
$_.IsIPv6LinkLocal -eq $IPv6LinkLocal
}).IPAddressToString)
})
}
end
{
# Dump to the Console
$IpInfo
}
}
#region LICENSE
<#
BSD 3-Clause License
Copyright (c) 2021, enabling Technology
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#>
#endregion LICENSE
#region DISCLAIMER
<#
DISCLAIMER:
- Use at your own risk, etc.
- This is open-source software, if you find an issue try to fix it yourself. There is no support and/or warranty in any kind
- This is a third-party Software
- The developer of this Software is NOT sponsored by or affiliated with Microsoft Corp (MSFT) or any of its subsidiaries in any way
- The Software is not supported by Microsoft Corp (MSFT)
- By using the Software, you agree to the License, Terms, and any Conditions declared and described above
- If you disagree with any of the terms, and any conditions declared: Just delete it and build your own solution
#>
#endregion DISCLAIMER

View File

@@ -0,0 +1,158 @@
function Get-etLatestNuGetRelease
{
<#
.SYNOPSIS
Get the latest published version of a given Module from a NuGet Repository
.DESCRIPTION
Get the latest published version of a given PowerShell Module from a NuGet Repository
.PARAMETER Project
Name of the Project, e.g. et.Office365
.PARAMETER Repository
NuGet Repository, default is the PowerShell Gallery
.PARAMETER Version
Return a PowerShell Version String instead of a String
.EXAMPLE
PS C:\> Get-etLatestNuGetRelease -Project 'et.Office365'
Get the latest published version of a given Module from a NuGet Repository
.EXAMPLE
PS C:\> Get-etLatestNuGetRelease -Project 'et.Office365' -version
Get the latest published version of a given Module from a NuGet Repository, but as Version instead of a String
.EXAMPLE
PS C:\> 'et.Office365' | Get-etLatestNuGetRelease
Get the latest published version of a given Module from a NuGet Repository
.NOTES
enabling Technology internal Build helper function
.LINK
Get-etModuleVersion
.LINK
Compare-enModuleVersions
.LINK
Find-Module
#>
[CmdletBinding(ConfirmImpact = 'None')]
[OutputType([string])]
param
(
[Parameter(Mandatory,
ValueFromPipeline,
ValueFromPipelineByPropertyName,
Position = 1,
HelpMessage = 'Name of the Project, e.g. et.Office365')]
[ValidateNotNullOrEmpty()]
[Alias('etProject')]
[string]
$Project,
[Parameter(ValueFromPipeline,
ValueFromPipelineByPropertyName,
Position = 2)]
[ValidateNotNullOrEmpty()]
[Alias('etRepository', 'Gallery', 'NuGetGallery')]
[string]
$Repository = 'PSGallery',
[Parameter(ValueFromPipeline,
ValueFromPipelineByPropertyName,
Position = 3)]
[Alias('enVersion')]
[switch]
$Version = $false
)
begin
{
$LatestNuGetRelease = $null
}
process
{
try
{
$paramFindModule = @{
Name = $Project
Repository = $Repository
ErrorAction = 'Stop'
WarningAction = 'SilentlyContinue'
}
$LatestNuGetRelease = (Find-Module @paramFindModule | Select-Object -ExpandProperty Version)
}
catch
{
# Get error record
[Management.Automation.ErrorRecord]$e = $_
# Retrieve information about runtime error
$info = [PSCustomObject]@{
Exception = $e.Exception.Message
Reason = $e.CategoryInfo.Reason
Target = $e.CategoryInfo.TargetName
Script = $e.InvocationInfo.ScriptName
Line = $e.InvocationInfo.ScriptLineNumber
Column = $e.InvocationInfo.OffsetInLine
}
# Output information. Post-process collected info, and log info (optional)
$info | Out-String | Write-Verbose
Write-Error -Message ($info.Exception) -TargetObject ($info.Target) -ErrorAction Stop
break
}
}
end
{
if ($Version)
{
[version]$LatestNuGetRelease = $LatestNuGetRelease
}
else
{
[string]$LatestNuGetRelease = $LatestNuGetRelease
}
# Dump to the console
$LatestNuGetRelease
}
}
#region LICENSE
<#
BSD 3-Clause License
Copyright (c) 2021, enabling Technology
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#>
#endregion LICENSE
#region DISCLAIMER
<#
DISCLAIMER:
- Use at your own risk, etc.
- This is open-source software, if you find an issue try to fix it yourself. There is no support and/or warranty in any kind
- This is a third-party Software
- The developer of this Software is NOT sponsored by or affiliated with Microsoft Corp (MSFT) or any of its subsidiaries in any way
- The Software is not supported by Microsoft Corp (MSFT)
- By using the Software, you agree to the License, Terms, and any Conditions declared and described above
- If you disagree with any of the terms, and any conditions declared: Just delete it and build your own solution
#>
#endregion DISCLAIMER

View File

@@ -0,0 +1,131 @@
function Grant-LogOnAsService
{
<#
.SYNOPSIS
Grant user log on as a service right in PowerShell
.DESCRIPTION
Grant user log on as a service right in PowerShell
.PARAMETER Users
The User that should get the grant
.INPUTS
String, Multi Value is OK here
.OUTPUTS
None
.EXAMPLE
PS C:\> Grant-LogOnAsService -Users 'johndoe'
Grant user log on as a service right in PowerShell
.LINK
https://gist.github.com/ned1313/9143039
.NOTES
Just a minor refactoring of the original
#>
[CmdletBinding(ConfirmImpact = 'Low',
SupportsShouldProcess)]
param
(
[Parameter(Mandatory,
ValueFromPipeline,
ValueFromPipelineByPropertyName,
Position = 1,
HelpMessage = 'The User that should get the grant')]
[ValidateNotNullOrEmpty()]
[string[]]
$Users
)
process
{
if ($pscmdlet.ShouldProcess('Apply login as a service', "$Users"))
{
# Get list of currently used SIDs
& "$env:windir\system32\secedit.exe" /export /cfg tempexport.inf
$curSIDs = (Select-String -Path .\tempexport.inf -Pattern 'SeServiceLogonRight')
$Sids = $curSIDs.line
$sidstring = ''
foreach ($user in $Users)
{
$objUser = (New-Object -TypeName System.Security.Principal.NTAccount -ArgumentList ($user))
$strSID = $objUser.Translate([Security.Principal.SecurityIdentifier])
if (!$Sids.Contains($strSID) -and !$Sids.Contains($user))
{
$sidstring += ",*$strSID"
}
}
if ($sidstring)
{
$newSids = $Sids + $sidstring
Write-Output -InputObject ('New Sids: {0}' -f $newSids)
$tempinf = (Get-Content -Path tempexport.inf)
$tempinf = $tempinf.Replace($Sids, $newSids)
$null = (Add-Content -Path tempimport.inf -Value $tempinf -Force -Confirm:$false)
& "$env:windir\system32\secedit.exe" /import /db secedit.sdb /cfg '.\tempimport.inf'
& "$env:windir\system32\secedit.exe" /configure /db secedit.sdb
& "$env:windir\system32\gpupdate.exe" /force
}
else
{
Write-Output -InputObject 'No new sids'
}
}
}
end
{
if ($pscmdlet.ShouldProcess('Cleanup', 'Tempfiles'))
{
# Splat the Defaults
$paramRemoveItem = @{
Force = $true
Confirm = $false
ErrorAction = 'SilentlyContinue'
}
$null = (Remove-Item -Path '.\tempimport.inf' @paramRemoveItem)
$null = (Remove-Item -Path '.\secedit.sdb' @paramRemoveItem)
$null = (Remove-Item -Path '.\tempexport.inf' @paramRemoveItem)
}
}
}
#region LICENSE
<#
BSD 3-Clause License
Copyright (c) 2021, enabling Technology
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#>
#endregion LICENSE
#region DISCLAIMER
<#
DISCLAIMER:
- Use at your own risk, etc.
- This is open-source software, if you find an issue try to fix it yourself. There is no support and/or warranty in any kind
- This is a third-party Software
- The developer of this Software is NOT sponsored by or affiliated with Microsoft Corp (MSFT) or any of its subsidiaries in any way
- The Software is not supported by Microsoft Corp (MSFT)
- By using the Software, you agree to the License, Terms, and any Conditions declared and described above
- If you disagree with any of the terms, and any conditions declared: Just delete it and build your own solution
#>
#endregion DISCLAIMER

View File

@@ -0,0 +1,439 @@
function Add-HostsEntry
{
<#
.SYNOPSIS
Add a single Hosts Entry to the HOSTS File
.DESCRIPTION
Add a single Hosts Entry to the HOSTS File, multiple are not supported yet!
.PARAMETER Path
The path to the hosts file where the entry should be set. Defaults to the local computer's hosts file.
.PARAMETER Address
The Address address for the hosts entry.
.PARAMETER HostName
The hostname for the hosts entry.
.PARAMETER force
Force (replace)
.EXAMPLE
PS C:\> Add-HostsEntry -Address '0.0.0.0' -HostName 'badhost'
Add the host 'badhost' with the Adress '0.0.0.0' (blackhole) wo the Hosts.
If an Entry for 'badhost' exists, the new one will be appended anyway (You end up with two entries)
.EXAMPLE
PS C:\> Add-HostsEntry -Address '0.0.0.0' -HostName 'badhost' -force
Add the host 'badhost' with the Adress '0.0.0.0' (blackhole) wo the Hosts.
If an Entry for 'badhost' exists, the new one will replace the existing one.
.NOTES
Internal Helper, inspired by an old GIST I found
.LINK
Get-HostsFile
.LINK
Remove-HostsEntry
.LINK
https://gist.github.com/markembling/173887/1824b370be3fe468faceaed5f39b12bad010a417
#>
[CmdletBinding(ConfirmImpact = 'Medium',
SupportsShouldProcess)]
param
(
[Parameter(Mandatory,
Position = 0,
HelpMessage = 'The IP address for the hosts entry.')]
[ValidateNotNullOrEmpty()]
[Alias('ipaddress', 'ip')]
[string]
$Address,
[Parameter(Mandatory,
ValueFromPipeline,
ValueFromPipelineByPropertyName,
Position = 1,
HelpMessage = 'The hostname for the hosts entry.')]
[ValidateNotNullOrEmpty()]
[Alias('Host', 'Name')]
[string]
$HostName,
[Parameter(ValueFromPipeline,
ValueFromPipelineByPropertyName,
Position = 3)]
[switch]
$force = $false,
[Parameter(ValueFromPipeline,
ValueFromPipelineByPropertyName,
Position = 2)]
[ValidateNotNullOrEmpty()]
[Alias('filename', 'Hosts', 'hostsfile', 'file')]
[string]
$Path = "$env:windir\System32\drivers\etc\hosts"
)
begin
{
Write-Verbose -Message 'Start'
}
process
{
if ($force)
{
try
{
$null = (Remove-HostsEntry -HostName $HostName -Path $Path -ErrorAction SilentlyContinue -WarningAction SilentlyContinue)
}
catch
{
Write-Verbose -Message 'Looks like the entry was not there before'
}
}
try
{
if ($pscmdlet.ShouldProcess('Target', 'Operation'))
{
# Get a clean (end of) file
$paramGetContent = @{
Path = $Path
Raw = $true
Force = $true
ErrorAction = 'Stop'
WarningAction = 'SilentlyContinue'
}
$HostsFileContent = (((Get-Content @paramGetContent ).TrimEnd()).ToString())
$NewValue = "`n" + $Address + "`t`t" + $HostName
$NewHostsFileContent = $HostsFileContent + $NewValue
$paramSetContent = @{
Path = $Path
Value = $NewHostsFileContent
Force = $true
Confirm = $false
Encoding = 'UTF8'
ErrorAction = 'Stop'
WarningAction = 'SilentlyContinue'
}
$null = (Set-Content @paramSetContent)
}
}
catch
{
# get error record
[Management.Automation.ErrorRecord]$e = $_
# retrieve information about runtime error
$info = [PSCustomObject]@{
Exception = $e.Exception.Message
Reason = $e.CategoryInfo.Reason
Target = $e.CategoryInfo.TargetName
Script = $e.InvocationInfo.ScriptName
Line = $e.InvocationInfo.ScriptLineNumber
Column = $e.InvocationInfo.OffsetInLine
}
$info | Out-String | Write-Verbose
Write-Error -Message ($info.Exception) -ErrorAction Stop
# Only here to catch a global ErrorAction overwrite
break
}
}
end
{
Write-Verbose -Message 'Done'
}
}
function Remove-HostsEntry
{
<#
.SYNOPSIS
Removes a single Hosts Entry from the HOSTS File
.DESCRIPTION
Removes a single Hosts Entry from the HOSTS File, multiple are not supported yet!
.PARAMETER Path
The path to the hosts file where the entry should be set. Defaults to the local computer's hosts file.
.PARAMETER HostName
The hostname for the hosts entry.
.EXAMPLE
PS C:\> Remove-HostsEntry -HostName 'Dummy'
Remove the entry for the host 'Dummy' from the HOSTS File
.NOTES
Internal Helper, inspired by an old GIST I found
.LINK
Get-HostsFile
.LINK
Add-HostsEntry
.LINK
https://gist.github.com/markembling/173887/1824b370be3fe468faceaed5f39b12bad010a417
#>
[CmdletBinding(ConfirmImpact = 'Medium',
SupportsShouldProcess)]
param
(
[Parameter(Mandatory,
ValueFromPipeline,
ValueFromPipelineByPropertyName,
Position = 0,
HelpMessage = 'The hostname for the hosts entry.')]
[ValidateNotNullOrEmpty()]
[Alias('Host', 'Name')]
[string]
$HostName,
[Parameter(ValueFromPipeline,
ValueFromPipelineByPropertyName,
Position = 1)]
[ValidateNotNullOrEmpty()]
[Alias('Hosts', 'hostsfile', 'file', 'Filename')]
[string]
$Path = "$env:windir\System32\drivers\etc\hosts"
)
begin
{
Write-Verbose -Message 'Start'
try
{
$paramGetContent = @{
Path = $Path
Raw = $true
Force = $true
ErrorAction = 'Stop'
WarningAction = 'SilentlyContinue'
}
$HostsFileContent = (((Get-Content @paramGetContent ).TrimEnd()).ToString())
}
catch
{
# get error record
[Management.Automation.ErrorRecord]$e = $_
# retrieve information about runtime error
$info = [PSCustomObject]@{
Exception = $e.Exception.Message
Reason = $e.CategoryInfo.Reason
Target = $e.CategoryInfo.TargetName
Script = $e.InvocationInfo.ScriptName
Line = $e.InvocationInfo.ScriptLineNumber
Column = $e.InvocationInfo.OffsetInLine
}
$info | Out-String | Write-Verbose
Write-Error -Message ($info.Exception) -ErrorAction Stop
# Only here to catch a global ErrorAction overwrite
break
}
$newLines = @()
}
process
{
foreach ($line in $HostsFileContent)
{
$bits = [regex]::Split($line, '\t+')
if ($bits.count -eq 2)
{
if ($bits[1] -ne $HostName)
{
$newLines += $line
}
}
else
{
$newLines += $line
}
}
# Write file
try
{
if ($pscmdlet.ShouldProcess('Target', 'Operation'))
{
$paramClearContent = @{
Path = $Path
Force = $true
Confirm = $false
ErrorAction = 'Stop'
WarningAction = 'SilentlyContinue'
}
$null = (Clear-Content @paramClearContent)
$paramSetContent = @{
Path = $Path
Value = $newLines
Force = $true
Confirm = $false
Encoding = 'UTF8'
ErrorAction = 'Stop'
WarningAction = 'SilentlyContinue'
}
$null = (Set-Content @paramSetContent)
}
}
catch
{
# get error record
[Management.Automation.ErrorRecord]$e = $_
# retrieve information about runtime error
$info = [PSCustomObject]@{
Exception = $e.Exception.Message
Reason = $e.CategoryInfo.Reason
Target = $e.CategoryInfo.TargetName
Script = $e.InvocationInfo.ScriptName
Line = $e.InvocationInfo.ScriptLineNumber
Column = $e.InvocationInfo.OffsetInLine
}
$info | Out-String | Write-Verbose
Write-Error -Message ($info.Exception) -ErrorAction Stop
# Only here to catch a global ErrorAction overwrite
break
}
}
end
{
Write-Verbose -Message 'Done'
}
}
function Get-HostsFile
{
<#
.SYNOPSIS
Print the HOSTS File in a more clean format
.DESCRIPTION
Print the HOSTS File in a more clean format
.PARAMETER Path
The path to the hosts file where the entry should be set. Defaults to the local computer's hosts file.
.PARAMETER raw
Print raw Hosts File
.EXAMPLE
PS C:\> Get-HostsFile
Print the HOSTS File in a more clean format
.EXAMPLE
PS C:\> Get-HostsFile -raw
Print the HOSTS File in the regular format
.NOTES
Internal Helper, inspired by an old GIST I found
.LINK
Add-HostsEntry
.LINK
Remove-HostsEntry
.LINK
https://gist.github.com/markembling/173887/1824b370be3fe468faceaed5f39b12bad010a417
#>
[CmdletBinding(ConfirmImpact = 'None')]
param
(
[Parameter(ValueFromPipeline,
ValueFromPipelineByPropertyName,
Position = 0)]
[ValidateNotNullOrEmpty()]
[Alias('Hosts', 'hostsfile', 'file', 'filename')]
[string]
$Path = "$env:windir\System32\drivers\etc\hosts",
[Parameter(ValueFromPipeline,
ValueFromPipelineByPropertyName,
Position = 1)]
[Alias('plain')]
[switch]
$raw = $false
)
begin
{
$HostsFileContent = Get-Content -Path $Path
}
process
{
foreach ($line in $HostsFileContent)
{
if ($raw)
{
Write-Output -InputObject $line
}
else
{
$bits = [regex]::Split($line, '\t+')
if ($bits.count -eq 2)
{
[string]$HostsFileLine = $bits
if (-not ($HostsFileLine.StartsWith('#')))
{
Write-Output -InputObject $HostsFileLine
}
}
}
}
}
}
#region LICENSE
<#
BSD 3-Clause License
Copyright (c) 2021, enabling Technology
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#>
#endregion LICENSE
#region DISCLAIMER
<#
DISCLAIMER:
- Use at your own risk, etc.
- This is open-source software, if you find an issue try to fix it yourself. There is no support and/or warranty in any kind
- This is a third-party Software
- The developer of this Software is NOT sponsored by or affiliated with Microsoft Corp (MSFT) or any of its subsidiaries in any way
- The Software is not supported by Microsoft Corp (MSFT)
- By using the Software, you agree to the License, Terms, and any Conditions declared and described above
- If you disagree with any of the terms, and any conditions declared: Just delete it and build your own solution
#>
#endregion DISCLAIMER

View File

@@ -0,0 +1,183 @@
function Install-DSCResourceKit
{
<#
.SYNOPSIS
Install the complete PowerShell DSCResourceKit
.DESCRIPTION
Install the complete PowerShell DSCResourceKit from the PowerShell Gallery.
It only installs the missing resources.
.PARAMETER Scope
Specifies the installation scope of the module. The acceptable values for this parameter are: AllUsers and CurrentUser.
The AllUsers scope lets modules be installed in a location that is accessible to all users of the computer, that is, %systemdrive%:\ProgramFiles\WindowsPowerShell\Modules.
The CurrentUser scope lets modules be installed only to $home\Documents\WindowsPowerShell\Modules, so that the module is available only to the current user.
.EXAMPLE
PS C:\> Install-DSCResourceKit
Install the complete PowerShell DSCResourceKit
.EXAMPLE
PS C:\> Install-DSCResourceKit -verbose
Install the complete PowerShell DSCResourceKit
.NOTES
Releasenotes:
1.0.0 2019-04-10: Internal Release
THIS CODE IS MADE AVAILABLE AS IS, WITHOUT WARRANTY OF ANY KIND. THE ENTIRE RISK OF THE USE OR THE RESULTS FROM THE USE OF THIS CODE REMAINS WITH THE USER.
Dependencies:
PowerShellGet
.LINK
https://aka.ms/InstallModule
.LINK
https://www.powershellgallery.com
#>
[CmdletBinding(ConfirmImpact = 'None',
SupportsShouldProcess)]
param
(
[Parameter(ValueFromPipeline,
ValueFromPipelineByPropertyName,
Position = 0)]
[ValidateSet('AllUsers', 'CurrentUser', IgnoreCase = $true)]
[Alias('ModuleScope')]
[String]
$Scope = 'AllUsers'
)
begin
{
try
{
if (-not ($Scope))
{
$Scope = 'AllUsers'
}
$AllReSources = ((Find-Module -Tag DSCResourceKit).name)
$AllInstall = ((Get-Module -ListAvailable).Name)
}
catch
{
# Get error record
[Management.Automation.ErrorRecord]$e = $_
# Retrieve information about runtime error
$info = [PSCustomObject]@{
Exception = $e.Exception.Message
Reason = $e.CategoryInfo.Reason
Target = $e.CategoryInfo.TargetName
Script = $e.InvocationInfo.ScriptName
Line = $e.InvocationInfo.ScriptLineNumber
Column = $e.InvocationInfo.OffsetInLine
}
$info | Out-String | Write-Verbose
# Whoops
Write-Error -Message $info.Exception -ErrorAction Stop
}
}
process
{
if ($pscmdlet.ShouldProcess('DSCResourceKit', 'Install'))
{
foreach ($DSCResource in $AllReSources)
{
if (-not ($AllInstall.Contains($DSCResource)))
{
try
{
Write-Verbose -Message ('Try to install {0}' -f $DSCResource)
$paramInstallModule = @{
Name = $DSCResource
Scope = $Scope
AllowClobber = $true
SkipPublisherCheck = $true
Repository = 'PSGallery'
Force = $true
ErrorAction = 'Stop'
}
$null = (Install-Module @paramInstallModule)
Write-Verbose -Message ('Installed {0}' -f $DSCResource)
}
catch
{
# Get error record
[Management.Automation.ErrorRecord]$e = $_
# Retrieve information about runtime error
$info = [PSCustomObject]@{
Exception = $e.Exception.Message
Reason = $e.CategoryInfo.Reason
Target = $e.CategoryInfo.TargetName
Script = $e.InvocationInfo.ScriptName
Line = $e.InvocationInfo.ScriptLineNumber
Column = $e.InvocationInfo.OffsetInLine
}
$info | Out-String | Write-Verbose
Write-Warning -Message ('Unable to install {0}' -f $DSCResource) -ErrorAction Continue -WarningAction Continue
# Cleanup
$e = $null
$info = $null
}
}
else
{
Write-Verbose -Message ('{0} is already installed' -f $DSCResource)
}
}
}
}
end
{
# Cleanup
$AllReSources = $null
$AllInstall = $null
}
}
#region LICENSE
<#
BSD 3-Clause License
Copyright (c) 2021, enabling Technology
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#>
#endregion LICENSE
#region DISCLAIMER
<#
DISCLAIMER:
- Use at your own risk, etc.
- This is open-source software, if you find an issue try to fix it yourself. There is no support and/or warranty in any kind
- This is a third-party Software
- The developer of this Software is NOT sponsored by or affiliated with Microsoft Corp (MSFT) or any of its subsidiaries in any way
- The Software is not supported by Microsoft Corp (MSFT)
- By using the Software, you agree to the License, Terms, and any Conditions declared and described above
- If you disagree with any of the terms, and any conditions declared: Just delete it and build your own solution
#>
#endregion DISCLAIMER

View File

@@ -0,0 +1,360 @@
function Invoke-CheckPowerShellModules
{
<#
.SYNOPSIS
Check if one or more given modules are installed.
.DESCRIPTION
Check if one or more given modules are installed.
Any missing modules can be installed (optional) and updated to the latest version available on the PowerShell Gallery can be applied (optional).
.PARAMETER Module
One or more modules to check, update, install.
.PARAMETER Install
Install any missing modules from the PowerShell Gallery?
.PARAMETER Update
Updated to the latest PowerShell Gallery Version of the module, if available?
.PARAMETER Scope
Specifies the installation scope of the module.
The acceptable values for this parameter are: AllUsers and CurrentUser.
The default is CurrentUser.
The AllUsers scope lets modules be installed in a location that is accessible to all users of the computer,
that is, %systemdrive%:\ProgramFiles\WindowsPowerShell\Modules. Elevated Shell required!
The CurrentUser scope lets modules be installed only to $home\Documents\WindowsPowerShell\Modules,
so that the module is available only to the current user.
.EXAMPLE
PS C:\> Invoke-CheckPowerShellModules -Module 'MSOnline', 'azuread', 'AzureADPreview', 'Microsoft.Online.SharePoint.PowerShell', 'MicrosoftTeams', 'Microsoft.PowerApps.PowerShell', 'Microsoft.PowerApps.Administration.PowerShell', 'SharePointPnPPowerShellOnline', 'credentialmanager' -Install
Check if all the Office 365 related PowerShell Modules are installed.
This will not install anything missing; it just runs a check!
.EXAMPLE
PS C:\> Invoke-CheckPowerShellModules -Module 'MSOnline', 'azuread', 'AzureADPreview', 'Microsoft.Online.SharePoint.PowerShell', 'MicrosoftTeams', 'Microsoft.PowerApps.PowerShell', 'Microsoft.PowerApps.Administration.PowerShell', 'SharePointPnPPowerShellOnline', 'credentialmanager' -Install
Install all the Office 365 related PowerShell Modules if anything is missing.
.EXAMPLE
PS C:\> Invoke-CheckPowerShellModules -Module 'MSOnline', 'azuread', 'AzureADPreview', 'Microsoft.Online.SharePoint.PowerShell', 'MicrosoftTeams', 'Microsoft.PowerApps.PowerShell', 'Microsoft.PowerApps.Administration.PowerShell', 'SharePointPnPPowerShellOnline', 'credentialmanager' -Scope AllUsers
Install all the Office 365 related PowerShell Modules if anything is missing (system wide).
This required to runn in an elevated Shell!!!
.EXAMPLE
PS C:\> Invoke-CheckPowerShellModules -Module 'MSOnline', 'azuread', 'AzureADPreview', 'Microsoft.Online.SharePoint.PowerShell', 'MicrosoftTeams', 'Microsoft.PowerApps.PowerShell', 'Microsoft.PowerApps.Administration.PowerShell', 'SharePointPnPPowerShellOnline', 'credentialmanager' -Update
Install all the Office 365 related PowerShell Modules if missing, automatically updates the latest version (if there is any update available)
.NOTES
For now, only the PowerShell Gallery is supported as Repository!
The next version might bring the check for an elevated shell if the scope is set to 'AllUsers'.
Releasenotes:
1.0.1 2019-05-24: Make it a bit more robust and add some examples (intial public release)
1.0.0 2019-05-15: Initial Release (internal)
THIS CODE IS MADE AVAILABLE AS IS, WITHOUT WARRANTY OF ANY KIND. THE ENTIRE RISK OF THE USE OR THE RESULTS FROM THE USE OF THIS CODE REMAINS WITH THE USER.
#>
[CmdletBinding(ConfirmImpact = 'Low',
SupportsShouldProcess)]
param
(
[Parameter(Mandatory,
ValueFromPipeline,
ValueFromPipelineByPropertyName,
Position = 0,
HelpMessage = 'One or more Modules to check.')]
[ValidateNotNullOrEmpty()]
[string[]]
$Module,
[Parameter(ValueFromPipeline,
ValueFromPipelineByPropertyName,
Position = 1)]
[Alias('AutoInstall', 'InstallMissing')]
[switch]
$Install = $null,
[Parameter(ValueFromPipeline,
ValueFromPipelineByPropertyName,
Position = 2)]
[Alias('AutoUpdate')]
[switch]
$Update = $null,
[Parameter(ValueFromPipeline,
ValueFromPipelineByPropertyName,
Position = 3)]
[ValidateNotNullOrEmpty()]
[ValidateSet('AllUsers', 'CurrentUser', IgnoreCase = $true)]
[Alias('InstallScope', 'ModuleScope')]
[string]
$Scope = 'CurrentUser'
)
begin
{
# The default scope is the current user (if not given)
if (-not $Scope)
{
$Scope = 'CurrentUser'
}
# Mandatory PowerShell Modules for Office 365 administration.
if (-not $Module)
{
$Module = 'MSOnline', 'azuread', 'AzureADPreview', 'Microsoft.Online.SharePoint.PowerShell', 'MicrosoftTeams', 'Microsoft.PowerApps.PowerShell', 'Microsoft.PowerApps.Administration.PowerShell'
}
}
process
{
foreach ($PowerShellModule in $Module)
{
# Cleanup
$InstalledModuleVersion = $null
$LatestModuleVersion = $null
$UpdateVersion = $null
try
{
Write-Verbose -Message ('Start processing for {0}' -f $PowerShellModule)
# Cleanup
$InstalledModuleVersion = $null
# In some cases, we might have different versions installed.
# We just want to have the latest and greatest one.
$paramGetModule = @{
Name = $PowerShellModule
ListAvailable = $true
ErrorAction = 'Stop'
}
$InstalledModuleVersion = (Get-Module @paramGetModule | Select-Object -Property Name, Version, repositorysourcelocation | Sort-Object -Property Version -Descending | Select-Object -First 1)
if (-not $InstalledModuleVersion)
{
if ($Install)
{
Write-Verbose -Message ('Start the installation of {0}' -f $PowerShellModule)
try
{
if ($pscmdlet.ShouldProcess($PowerShellModule, 'Install'))
{
$paramInstallModule = @{
Name = $PowerShellModule
Repository = 'PSGallery'
ErrorAction = 'Stop'
WarningAction = 'Continue'
Scope = $Scope
Force = $true
AllowClobber = $true
}
$null = (Install-Module @paramInstallModule)
}
}
catch
{
# Get error record
[Management.Automation.ErrorRecord]$e = $_
# Build the Info object
$info = [PSCustomObject]@{
Exception = $e.Exception.Message
Reason = $e.CategoryInfo.Reason
Target = $e.CategoryInfo.TargetName
Script = $e.InvocationInfo.ScriptName
Line = $e.InvocationInfo.ScriptLineNumber
Column = $e.InvocationInfo.OffsetInLine
}
# Do some verbose things
$info | Out-String | Write-Verbose
$paramWriteError = @{
Message = $e.Exception.Message
ErrorAction = 'Stop'
Exception = $e.Exception
TargetObject = $e.CategoryInfo.TargetName
}
Write-Error @paramWriteError
}
Write-Verbose -Message ('Finished the installation of {0}' -f $PowerShellModule)
}
else
{
# Error message
Write-Error -Message ('{0} was not found...' -f $PowerShellModule) -Category NotInstalled -ErrorAction Stop
}
}
else
{
if ($InstalledModuleVersion.RepositorySourceLocation.Authority -ne 'www.powershellgallery.com')
{
Write-Error -Message ('Sorry, but only modules from the PowerShell Gallery are supported and {0} is not installed from there.' -f $PowerShellModule) -Category InvalidType -ErrorAction Stop
}
else
{
try
{
Write-Verbose -Message ('Get the latest PowerShell Gallery version for {0}' -f $PowerShellModule)
$paramFindModule = @{
Name = $PowerShellModule
Repository = 'PSGallery'
ErrorAction = 'Stop'
}
$LatestModuleVersion = (Find-Module @paramFindModule | Select-Object -Property Name, Version)
$UpdateVersion = $LatestModuleVersion.Version
Write-Verbose -Message ('Found version {0} of {1} in the PowerShell Gallery' -f $UpdateVersion, $PowerShellModule)
if ($InstalledModuleVersion.Version -ilt $UpdateVersion)
{
Write-Verbose -Message ('Version {0} for {1} is availible in the PowerShell Galery' -f $UpdateVersion, $PowerShellModule)
if ($Update)
{
Write-Verbose -Message ('Start the update for {0} to version {1}' -f $PowerShellModule, $UpdateVersion)
try
{
if ($pscmdlet.ShouldProcess($PowerShellModule, 'Update'))
{
$paramInstallModule = @{
Name = $PowerShellModule
Repository = 'PSGallery'
ErrorAction = 'Stop'
WarningAction = 'Continue'
Scope = $Scope
Force = $true
AllowClobber = $true
}
$null = (Install-Module @paramInstallModule)
}
Write-Verbose -Message ('Installed version {0} for {1}' -f $UpdateVersion, $PowerShellModule)
}
catch
{
# Get error record
[Management.Automation.ErrorRecord]$e = $_
# Create the Info Object
$info = [PSCustomObject]@{
Exception = $e.Exception.Message
Reason = $e.CategoryInfo.Reason
Target = $e.CategoryInfo.TargetName
Script = $e.InvocationInfo.ScriptName
Line = $e.InvocationInfo.ScriptLineNumber
Column = $e.InvocationInfo.OffsetInLine
}
# Do some verbose stuff
$info | Out-String | Write-Verbose
Write-Warning -Message $e.Exception.Message
}
}
else
{
Write-Warning -Message ('Version {0} for {1} is availible on the PowerShell Galery' -f $UpdateVersion, $PowerShellModule)
}
}
else
{
Write-Verbose -Message ('No update found for {0}' -f $PowerShellModule)
}
}
catch
{
# Get error record
[Management.Automation.ErrorRecord]$e = $_
# Create the Info Object
$info = [PSCustomObject]@{
Exception = $e.Exception.Message
Reason = $e.CategoryInfo.Reason
Target = $e.CategoryInfo.TargetName
Script = $e.InvocationInfo.ScriptName
Line = $e.InvocationInfo.ScriptLineNumber
Column = $e.InvocationInfo.OffsetInLine
}
# Do some verbose stuff
$info | Out-String | Write-Verbose
Write-Warning -Message $e.Exception.Message
}
}
}
}
catch
{
# Get error record
[Management.Automation.ErrorRecord]$e = $_
# Create the Info Object
$info = [PSCustomObject]@{
Exception = $e.Exception.Message
Reason = $e.CategoryInfo.Reason
Target = $e.CategoryInfo.TargetName
Script = $e.InvocationInfo.ScriptName
Line = $e.InvocationInfo.ScriptLineNumber
Column = $e.InvocationInfo.OffsetInLine
}
# Do some verbose stuff
$info | Out-String | Write-Verbose
$paramWriteError = @{
Message = $e.Exception.Message
ErrorAction = 'Stop'
Exception = $e.Exception
TargetObject = $e.CategoryInfo.TargetName
}
Write-Error @paramWriteError
}
}
}
end
{
Write-Verbose -Message 'Done'
}
}
#region LICENSE
<#
BSD 3-Clause License
Copyright (c) 2021, enabling Technology
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#>
#endregion LICENSE
#region DISCLAIMER
<#
DISCLAIMER:
- Use at your own risk, etc.
- This is open-source software, if you find an issue try to fix it yourself. There is no support and/or warranty in any kind
- This is a third-party Software
- The developer of this Software is NOT sponsored by or affiliated with Microsoft Corp (MSFT) or any of its subsidiaries in any way
- The Software is not supported by Microsoft Corp (MSFT)
- By using the Software, you agree to the License, Terms, and any Conditions declared and described above
- If you disagree with any of the terms, and any conditions declared: Just delete it and build your own solution
#>
#endregion DISCLAIMER

View File

@@ -0,0 +1,269 @@
#requires -Version 3.0 -Modules @{ ModuleName="PowerShellGet"; ModuleVersion="2.0.0" } -RunAsAdministrator
<#
.SYNOPSIS
Remove older versions of a installed PowerShell module
.DESCRIPTION
Remove older versions of a installed PowerShell module
.EXAMPLE
PS C:\> .\Invoke-CleanupOldGalleryModuleVersions.ps1
.EXAMPLE
PS C:\> .\Invoke-CleanupOldGalleryModuleVersions.ps1 -Verbose
.EXAMPLE
PS C:\> .\Invoke-CleanupOldGalleryModuleVersions.ps1 -Verbose
.EXAMPLE
PS C:\> .\Invoke-CleanupOldGalleryModuleVersions.ps1 -debug
.NOTES
This is a replacement for some older functions
.LINK
Invoke-UpdateAllGalleryModules.ps1
#>
[CmdletBinding(ConfirmImpact = 'Low',
SupportsShouldProcess)]
param ()
begin
{
#region Defaults
$STP = 'Stop'
$CNT = 'Continue'
$SCT = 'SilentlyContinue'
#endregion Defaults
#region Cleanup
$AllModules = $null
#endregion Cleanup
#region BoundParameters
if (($PSCmdlet.MyInvocation.BoundParameters['Verbose']).IsPresent)
{
$VerboseValue = $true
}
else
{
$VerboseValue = $false
}
if (($PSCmdlet.MyInvocation.BoundParameters['Debug']).IsPresent)
{
$DebugValue = $true
}
else
{
$DebugValue = $false
}
if (($PSCmdlet.MyInvocation.BoundParameters['WhatIf']).IsPresent)
{
$WhatIfValue = $true
}
else
{
$WhatIfValue = $false
}
#endregion BoundParameters
}
process
{
# Get the Module information
$paramGetModule = @{
ListAvailable = $true
Refresh = $true
ErrorAction = $CNT
WarningAction = $CNT
Verbose = $VerboseValue
Debug = $DebugValue
}
$AllModules = (Get-Module @paramGetModule | Where-Object -FilterScript {
$_.RepositorySourceLocation -like '*powershellgallery*'
} | Select-Object -ExpandProperty Name)
$AllModules = ($AllModules | Sort-Object -Unique)
foreach ($ModuleName in $AllModules)
{
Write-Verbose -Message ('Get all existing versions of {0}' -f $ModuleName)
$AllModuleVersions = $null
$AllModuleVersions = (Get-InstalledModule -Name $ModuleName -AllVersions -ErrorAction $SCT -WarningAction $CNT)
if (((($AllModuleVersions).Version).count) -gt 1)
{
$LatestModuleVersion = $null
$LatestModuleVersion = (($AllModuleVersions | Sort-Object -Property $AllModuleVersions.Version)[1])
try
{
$output = $null
$output = ($AllModuleVersions | Where-Object {
(($_.Version) -lt ($LatestModuleVersion.Version))
} | ForEach-Object -Process {
Write-Verbose -Message ('Start to process {0}' -f ($_).Name)
try
{
$paramUninstallModule = @{
Name = $_
Force = $true
Confirm = $false
WhatIf = $WhatIfValue
Verbose = $VerboseValue
Debug = $DebugValue
ErrorAction = $STP
WarningAction = $CNT
}
Uninstall-Module @paramUninstallModule
}
catch
{
# Get error record
[Management.Automation.ErrorRecord]$e = $_
# retrieve information about runtime error
$info = [PSCustomObject]@{
Exception = $e.Exception.Message
Reason = $e.CategoryInfo.Reason
Target = $e.CategoryInfo.TargetName
Script = $e.InvocationInfo.ScriptName
Line = $e.InvocationInfo.ScriptLineNumber
Column = $e.InvocationInfo.OffsetInLine
}
# output information. Post-process collected info, and log info (optional)
$info | Out-String | Write-Verbose
$paramWriteError = @{
Message = $e.Exception.Message
ErrorAction = $CNT
Exception = $e.Exception
TargetObject = $e.CategoryInfo.TargetName
}
Write-Error @paramWriteError
}
finally
{
if (Test-Path -Path $_.InstalledLocation -ErrorAction $SCT -WarningAction $SCT)
{
Write-Verbose -Message ('Try to remove {0}' -f ($_).InstalledLocation)
try
{
$paramRemoveItem = @{
Path = $_.InstalledLocation
Recurse = $true
Force = $true
Confirm = $false
ErrorAction = $STP
WarningAction = $CNT
WhatIf = $WhatIfValue
Verbose = $VerboseValue
Debug = $DebugValue
}
Remove-Item @paramRemoveItem
Write-Verbose -Message ('Removed {0}' -f ($_).InstalledLocation)
}
catch
{
# Get error record
[Management.Automation.ErrorRecord]$e = $_
# retrieve information about runtime error
$info = [PSCustomObject]@{
Exception = $e.Exception.Message
Reason = $e.CategoryInfo.Reason
Target = $e.CategoryInfo.TargetName
Script = $e.InvocationInfo.ScriptName
Line = $e.InvocationInfo.ScriptLineNumber
Column = $e.InvocationInfo.OffsetInLine
}
# output information. Post-process collected info, and log info (optional)
$info | Out-String | Write-Verbose
$paramWriteError = @{
Message = $e.Exception.Message
ErrorAction = $STP
Exception = $e.Exception
TargetObject = $e.CategoryInfo.TargetName
}
Write-Error @paramWriteError
}
}
}
Write-Verbose -Message ('Removed old versions off {0}' -f ($_).Name)
})
$output
}
catch
{
# Get error record
[Management.Automation.ErrorRecord]$e = $_
# retrieve information about runtime error
$info = [PSCustomObject]@{
Exception = $e.Exception.Message
Reason = $e.CategoryInfo.Reason
Target = $e.CategoryInfo.TargetName
Script = $e.InvocationInfo.ScriptName
Line = $e.InvocationInfo.ScriptLineNumber
Column = $e.InvocationInfo.OffsetInLine
}
# output information. Post-process collected info, and log info (optional)
$info | Out-String | Write-Verbose
Write-Warning -Message ('Failed to process {0}' -f ($_).Name)
}
}
else
{
Write-Verbose -Message ('Skip {0}' -f ($AllModuleVersions).Name)
}
}
}
end
{
$AllModules = $null
}
#region LICENSE
<#
BSD 3-Clause License
Copyright (c) 2021, enabling Technology
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#>
#endregion LICENSE
#region DISCLAIMER
<#
DISCLAIMER:
- Use at your own risk, etc.
- This is open-source software, if you find an issue try to fix it yourself. There is no support and/or warranty in any kind
- This is a third-party Software
- The developer of this Software is NOT sponsored by or affiliated with Microsoft Corp (MSFT) or any of its subsidiaries in any way
- The Software is not supported by Microsoft Corp (MSFT)
- By using the Software, you agree to the License, Terms, and any Conditions declared and described above
- If you disagree with any of the terms, and any conditions declared: Just delete it and build your own solution
#>
#endregion DISCLAIMER

View File

@@ -0,0 +1,302 @@
#requires -Version 3.0 -Modules DnsClient
<#
.SYNOPSIS
Update CloudFlare DNS A Record if needed
.DESCRIPTION
Update CloudFlare DNS A Record if needed
The prevent to much API calls, we use a regular DNS query first.
Only if this query spot a difference, we ensure if an update is needed by ask the Cloudflare API for the latest published info.
If there is stiff a difference, the cmdlet will update the entry for you.
If you use a new/unknown hostname in the CF_HOSTNAME parameter, the cmdlet will create a new entry for the given host!
.PARAMETER CF_TOKEN
CloudFlare API Token
Hint: You can find your API key at: https://dash.cloudflare.com/profile/api-tokens
Create a dedicated Token just for this cmdlet and give it a name that indicate the purpose of it
The Token needs a least the following permission: Zone.Zone, Zone.DNS
The token needs access to at least the Zone you want to update (Resources), or use 'All zones'
.PARAMETER CF_DOMAIN
The CloudFlare DNS zone you want to modify
Example: contoso.com (this is also the default)
.PARAMETER CF_HOSTNAME
This is the A record you'd like to update or add
Example: homelab (this is also the default)
Please Note: We support A Records only at this time!
.PARAMETER DNSServer
Resolves hostname using DNS instead of checking CloudFlare.
It is recommended to use the CloudFlare DNS Servers, e.g. 1.1.1.1
You can use any other server, but mind that you might not see the changed IP until the Cache TTL expired on this Server!
.EXAMPLE
PS C:\> .\Invoke-CloudFlareDDNSUpdate.ps1 -CF_TOKEN '<TOKEN>' -CF_DOMAIN 'contoso.com' -CF_HOSTNAME 'homelab'
Check and updates the host 'homelab' in the DNS Zone 'contoso.com'
.EXAMPLE
PS C:\> .\Invoke-CloudFlareDDNSUpdate.ps1 -CF_TOKEN '<TOKEN>' -CF_DOMAIN 'contoso.com' -CF_HOSTNAME 'homelab' -Verbose
Check and updates the host 'homelab' in the DNS Zone 'contoso.com', but run in verbose mode
.EXAMPLE
PS C:\> .\Invoke-CloudFlareDDNSUpdate.ps1 -CF_TOKEN '<TOKEN>' -CF_DOMAIN 'contoso.com' -CF_HOSTNAME 'homelab' -DNSServer 1.0.0.1
Check and updates the host 'homelab' in the DNS Zone 'contoso.com', uses the backup CloudFlare DNS to get the published info
.LINK
https://1.1.1.1/dns/
.NOTES
We use a regular (cheap) DNS call to reduce the number of calls to CloudFlare (they allow 200 reqs/minute but why ask an API first?)
There is no output by the cmdlet, makes it easier if run a a service or schedules task. use the -Verbose switch to see what the cmdlet is doing
Please Note: We support A Records only at this time! We are already testing IPv6 (AAAA) and a few others.
#>
[CmdletBinding(ConfirmImpact = 'None')]
param
(
[Parameter(ValueFromPipeline,
ValueFromPipelineByPropertyName)]
[ValidateNotNullOrEmpty()]
[Alias('CLOUDFLARE_TOKEN', 'CFAPIKey', 'Token')]
[string]
$CF_TOKEN = '<Your_Super_Secret_Token_Here>',
[Parameter(ValueFromPipeline,
ValueFromPipelineByPropertyName)]
[ValidateNotNullOrEmpty()]
[Alias('CLOUDFLARE_Domain', 'CLOUDFLARE_DomainName', 'CFDomainName', 'Zone')]
[string]
$CF_DOMAIN = 'contoso.com',
[Parameter(ValueFromPipeline,
ValueFromPipelineByPropertyName)]
[ValidateNotNullOrEmpty()]
[Alias('CFARecord', 'CLOUDFLARE_HOST', 'CLOUDFLARE_HOSTNAME')]
[string]
$CF_HOSTNAME = 'homelab',
[Parameter(ValueFromPipeline,
ValueFromPipelineByPropertyName)]
[ValidateNotNullOrEmpty()]
[Alias('DNSToUse')]
[string]
$DNSServer = '1.1.1.1'
)
begin
{
#region Cleanup
$CF_KnownIP = $null
$CF_ExternalIP = $null
#endregion Cleanup
#region CheapRequests
if (Get-Command -Name Resolve-DnsName -ErrorAction SilentlyContinue)
{
# Get the A record from the CloudFlare DNS (cheap request)
$paramResolveDnsName = @{
Name = ($CF_HOSTNAME + '.' + $CF_DOMAIN)
Type = 'A'
Server = $DNSServer
ErrorAction = 'SilentlyContinue'
WarningAction = 'Continue'
}
[string]$CF_KnownIP = (((Resolve-DnsName @paramResolveDnsName) | Select-Object -ExpandProperty IPAddress).Trim())
}
elseif (Get-Command -Name dig -ErrorAction SilentlyContinue)
{
# This is the Fallback on macOS, due to the missing DnsClient module on PowerShell core here
[string]$CF_KnownIP = (((dig A ($CF_HOSTNAME + '.' + $CF_DOMAIN) ('@' + $DNSServer) +short)).Trim())
}
else
{
Write-Warning -Message 'Unable to lookup the DNS entry, we try to use the CloudFlare API' -WarningAction Continue
# Set a dummy (to prevent any null pointer exception during the compare)
[string]$CF_KnownIP = '0.0.0.0'
}
# Get the external IP via Web Request from our own service (cheap request)
$paramInvokeRestMethod = @{
Method = 'Get'
UseBasicParsing = $true
Uri = 'https://ip.enatec.net'
ErrorAction = 'Stop'
WarningAction = 'Continue'
}
[string]$CF_ExternalIP = ((Invoke-RestMethod @paramInvokeRestMethod).Trim())
#endregion CheapRequests
}
process
{
# Compare the two values
if ($CF_ExternalIP -ne $CF_KnownIP)
{
# Looks like there is a Difference
# Only the V4 API is supported by the cmdlet yet!
$CF_API_ENDPOINT = $null
$CF_API_ENDPOINT = 'https://api.cloudflare.com/client/v4'
$CF_Headers = $null
$CF_Headers = @{
'Authorization' = ('Bearer ' + $CF_TOKEN)
'Content-Type' = 'application/json'
}
$CF_ZoneURI = $null
$CF_ZoneURI = ($CF_API_ENDPOINT + '/zones?name=' + $CF_DOMAIN)
Write-Verbose -Message ('Getting DNS-Zone ID for ' + $($CF_DOMAIN) + ' via ' + $CF_ZoneURI)
# Let us get the Zone Info directly from CloudFlare (API Request)
$CF_ZoneId = $null
$paramInvokeRestMethod = @{
Uri = $CF_ZoneURI
ContentType = 'application/json'
Headers = $CF_Headers
ErrorAction = 'Stop'
WarningAction = 'Continue'
}
$CF_ZoneId = (((Invoke-RestMethod @paramInvokeRestMethod).result).id)
$CF_DNSURI = $null
$CF_DNSURI = ($CF_API_ENDPOINT + '/zones/' + $CF_ZoneId + '/dns_records?type=A&name=' + $CF_HOSTNAME + '.' + $CF_DOMAIN)
Write-Verbose -Message ('Getting DNS data for ' + $($CF_HOSTNAME).$($CF_DOMAIN) + ' via ' + $CF_DNSURI)
# Let us get the host Info directly from CloudFlare (API Request)
$CF_DNSData = $null
$paramInvokeRestMethod = @{
Uri = $CF_DNSURI
ContentType = 'application/json'
Headers = $CF_Headers
ErrorAction = 'Stop'
WarningAction = 'Continue'
}
$CF_DNSData = ((Invoke-RestMethod @paramInvokeRestMethod).result)
# Compare again (Double check)
if ($CF_ExternalIP -ne $CF_DNSData.content)
{
# OK, we are sure that there is a new IP!
Write-Verbose -Message 'IP address change detected, we will try to update the CloudFlare DNS'
try
{
$CF_Body = $null
$CF_Body = @{
'type' = 'A'
'name' = ($CF_HOSTNAME + '.' + $CF_DOMAIN)
'content' = $CF_ExternalIP
'ttl' = '1'
}
$URI_Update = $null
$URI_Update = ($CF_API_ENDPOINT + '/zones/' + $CF_ZoneId + '/dns_records/' + $($CF_DNSData.id))
# Apply the new IP address to the CloudFlare DNS
$CF_Result = $null
$paramInvokeRestMethod = @{
Uri = $URI_Update
Method = 'Put'
ContentType = 'application/json'
Headers = $CF_Headers
Body = $
WebSession = ($CF_Body | ConvertTo-Json)
ErrorAction = 'Stop'
WarningAction = 'Continue'
}
$CF_Result = ((Invoke-RestMethod @paramInvokeRestMethod).result)
if ($CF_Result.content -eq $CF_ExternalIP)
{
Write-Verbose -Message 'SUCCESS: CloudFlare DNS was successfully updated'
}
else
{
Write-Verbose -Message 'FAILED: CloudFlare DNS was not successfully updated'
}
}
catch
{
# Get error record
[Management.Automation.ErrorRecord]$e = $_
# retrieve information about runtime error
$info = [PSCustomObject]@{
Exception = $e.Exception.Message
Reason = $e.CategoryInfo.Reason
Target = $e.CategoryInfo.TargetName
Script = $e.InvocationInfo.ScriptName
Line = $e.InvocationInfo.ScriptLineNumber
Column = $e.InvocationInfo.OffsetInLine
}
# output information. Post-process collected info, and log info (optional)
$info | Out-String | Write-Verbose
$paramWriteError = @{
Message = $e.Exception.Message
ErrorAction = 'Stop'
Exception = $e.Exception
TargetObject = $e.CategoryInfo.TargetName
}
Write-Error @paramWriteError
# Just in case
exit 1
}
}
else
{
Write-Verbose -Message 'CloudFlare: No update is needed'
}
}
else
{
Write-Verbose -Message 'DNS: No update is needed'
}
}
#region LICENSE
<#
BSD 3-Clause License
Copyright (c) 2021, enabling Technology
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#>
#endregion LICENSE
#region DISCLAIMER
<#
DISCLAIMER:
- Use at your own risk, etc.
- This is open-source software, if you find an issue try to fix it yourself. There is no support and/or warranty in any kind
- This is a third-party Software
- The developer of this Software is NOT sponsored by or affiliated with Microsoft Corp (MSFT) or any of its subsidiaries in any way
- The Software is not supported by Microsoft Corp (MSFT)
- By using the Software, you agree to the License, Terms, and any Conditions declared and described above
- If you disagree with any of the terms, and any conditions declared: Just delete it and build your own solution
#>
#endregion DISCLAIMER

View File

@@ -0,0 +1,159 @@
function Invoke-DSCPerfReqConfigCheck
{
<#
.SYNOPSIS
Perform Required Configuration Checks and suppress all outputs.
.DESCRIPTION
Run the DSCLocalConfigurationManager method PerformRequiredConfigurationChecks.
.PARAMETER Silent
The progress bar will be suppressed. this is not the case by default.
.EXAMPLE
PS C:\> Invoke-DSCPerfReqConfigCheck
True
# Run without any error
.EXAMPLE
PS C:\> Invoke-DSCPerfReqConfigCheck -Silent
True
# Run without any error. Suppress the progress bar.
.EXAMPLE
PS C:\> Invoke-DSCPerfReqConfigCheck
False
# The run had errors.
.EXAMPLE
PS C:\> Invoke-DSCPerfReqConfigCheck -Silent
False
# The run had errors. Suppress the progress bar.
.NOTES
I do a lot of testing with several DSC configurations.
I just want a TRUE or FALSE as return to see if its working, or not.
You may guess why: I use this in a CI chain :-)
You may want to have separated EventLog entries for DSC (useful for the log-Resource):
& "$env:windir\system32\wevtutil.exe" set-log 'Microsoft-Windows-Dsc/Analytic' /q:true /e:true
& "$env:windir\system32\wevtutil.exe" set-log 'Microsoft-Windows-Dsc/Debug' /q:True /e:true
I dedicate any and all copyright interest in this software to the public domain.
I make this dedication for the benefit of the public at large and to the detriment of my heirs and successors.
I intend this dedication to be an overt act of relinquishment in perpetuity of all present and future rights to this software under copyright law.
.LINK
Author http://jhochwald.com
.LINK
LICENSE http://unlicense.org
.LINK
Invoke-CimMethod
Write-Verbose
Get-WinEvent
#>
[OutputType([bool])]
param
(
[Parameter(ValueFromPipeline,
Position = 1)]
[switch]
$Silent = $null
)
begin
{
$SC = 'SilentlyContinue'
if ($Silent)
{
$ProgressPreference = $SC
}
}
process
{
$InvokeCimMethodParams = @{
Namespace = 'root/Microsoft/Windows/DesiredStateConfiguration'
ClassName = 'MSFT_DSCLocalConfigurationManager'
MethodName = 'PerformRequiredConfigurationChecks'
Arguments = @{
Flags = [uint32] 1
}
ErrorAction = $SC
WarningAction = $SC
}
try
{
$null = (Invoke-CimMethod @InvokeCimMethodParams)
if ($Silent)
{
$ProgressPreference = $null
}
}
catch
{
$paramWriteVerbose = @{
Message = "$_.Exception.Message - Line Number: $_.InvocationInfo.ScriptLineNumber"
ErrorAction = $SC
WarningAction = $SC
}
Write-Verbose @paramWriteVerbose
}
$GetWinEventParams = @{
LogName = 'Microsoft-Windows-Dsc/*'
ErrorAction = $SC
WarningAction = $SC
Oldest = $true
}
# TODO: That is fast, but the code looks bad!
$SuccessResult = (Get-WinEvent @GetWinEventParams | Group-Object -Property {
$_.Properties[0].value
}).Group.LevelDisplayName -notcontains 'Error'
}
end
{
return $SuccessResult
}
}
#region LICENSE
<#
BSD 3-Clause License
Copyright (c) 2021, enabling Technology
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#>
#endregion LICENSE
#region DISCLAIMER
<#
DISCLAIMER:
- Use at your own risk, etc.
- This is open-source software, if you find an issue try to fix it yourself. There is no support and/or warranty in any kind
- This is a third-party Software
- The developer of this Software is NOT sponsored by or affiliated with Microsoft Corp (MSFT) or any of its subsidiaries in any way
- The Software is not supported by Microsoft Corp (MSFT)
- By using the Software, you agree to the License, Terms, and any Conditions declared and described above
- If you disagree with any of the terms, and any conditions declared: Just delete it and build your own solution
#>
#endregion DISCLAIMER

View File

@@ -0,0 +1,315 @@
#requires -Version 3.0 -Modules @{ ModuleName="PowerShellGet"; ModuleVersion="2.0.0" } -RunAsAdministrator
<#
.SYNOPSIS
Update all PowerShell Modules to the latest PowerShell Gallery version
.DESCRIPTION
Update all PowerShell Modules to the latest PowerShell Gallery version
.PARAMETER Silent
Hide the PowerShell Progress Bars
.EXAMPLE
PS C:\> .\Invoke-UpdateAllGalleryModules.ps1
Update all PowerShell Modules to the latest PowerShell Gallery version
.EXAMPLE
PS C:\> .\Invoke-UpdateAllGalleryModules.ps1 -Silent
Update all PowerShell Modules to the latest PowerShell Gallery version and hide the PowerShell Progress Bars
.EXAMPLE
PS C:\> .\Invoke-UpdateAllGalleryModules.ps1 -WhatIf
Dry run the update all PowerShell Modules to the latest PowerShell Gallery version
.EXAMPLE
PS C:\> .\Invoke-UpdateAllGalleryModules.ps1 -verbose
Update all PowerShell Modules to the latest PowerShell Gallery version in verbose mode
.EXAMPLE
PS C:\> .\Invoke-UpdateAllGalleryModules.ps1 -verbose
Update all PowerShell Modules to the latest PowerShell Gallery version in debug mode
.NOTES
This is a replacement for some older functions
.LINK
Invoke-CleanupOldGalleryModuleVersions.ps1
#>
[CmdletBinding(ConfirmImpact = 'None',
SupportsShouldProcess)]
param
(
[Parameter(ValueFromPipeline,
ValueFromPipelineByPropertyName)]
[Alias('NoProgressBars')]
[switch]
$Silent
)
begin
{
#region Defaults
$STP = 'Stop'
$CNT = 'Continue'
$SCT = 'SilentlyContinue'
#endregion Defaults
#region Cleanup
$OriginalProgressPreference = $null
$AllModules = $null
#endregion Cleanup
#region BoundParameters
if (($PSCmdlet.MyInvocation.BoundParameters['Verbose']).IsPresent)
{
$VerboseValue = $true
}
else
{
$VerboseValue = $false
}
if (($PSCmdlet.MyInvocation.BoundParameters['Debug']).IsPresent)
{
$DebugValue = $true
}
else
{
$DebugValue = $false
}
if (($PSCmdlet.MyInvocation.BoundParameters['WhatIf']).IsPresent)
{
$WhatIfValue = $true
}
else
{
$WhatIfValue = $false
}
#endregion BoundParameters
if (($PSCmdlet.MyInvocation.BoundParameters['Silent']).IsPresent)
{
# Save the original value
$OriginalProgressPreference = $ProgressPreference
# Silence is golden...
$ProgressPreference = $SCT
}
# Get the Module information
$paramGetModule = @{
ListAvailable = $true
Refresh = $true
ErrorAction = $CNT
WarningAction = $CNT
Verbose = $VerboseValue
Debug = $DebugValue
}
$AllModules = (Get-Module @paramGetModule | Where-Object -FilterScript {
$_.RepositorySourceLocation -like '*powershellgallery*'
} | Select-Object -Property Name, Version, Path)
}
process
{
foreach ($SingleModule in $AllModules)
{
# Cleanup
$RepositoryInfo = $null
<#
The AllowPrerelease is needed here
Find-Module ignored the ErrorAction setting, try/catch will not work
#>
$paramFindModule = @{
Name = (($SingleModule).Name)
Repository = 'PSGallery'
AllowPrerelease = $true
ErrorAction = $SCT
WarningAction = $CNT
Verbose = $VerboseValue
Debug = $DebugValue
}
$RepositoryInfo = (Find-Module @paramFindModule | Select-Object -Property Name, Version)
#region CleanVersions
<#
Remove everything from the version string that violates the System.Version class
https://docs.microsoft.com/en-us/dotnet/api/system.version
e.g. -beta4 or -preview
#>
# Character that we use as a slipt
$SlipPointer = '-'
# Create the Wildcard to search for
$SplitSearch = ('*' + $SlipPointer + '*')
if (($SingleModule.Version) -like $SplitSearch)
{
$SingleModule.Version = (($SingleModule.Version).split($SlipPointer)[0])
}
if (($RepositoryInfo.Version) -like $SplitSearch)
{
$RepositoryInfo.Version = (($RepositoryInfo.Version).split($SlipPointer)[0])
}
#endregion CleanVersions
# Is the online version newer?
if ((($SingleModule).Version) -lt (($RepositoryInfo).Version))
{
# Cleanup
$ModuleScope = $null
# try to figure out the scope
if ((($SingleModule).Path) -like ($env:ProgramW6432 + '\*'))
{
$ModuleScope = 'AllUsers'
}
else
{
$ModuleScope = 'CurrentUser'
}
try
{
Write-Verbose -Message ('Try to update {0}' -f ($SingleModule).Name)
# Cleanup
$paramUpdateModule = $null
# Try the Update
$paramUpdateModule = @{
Name = (($SingleModule).Name)
Scope = $ModuleScope
Force = $true
AcceptLicense = $true
Confirm = $false
Verbose = $VerboseValue
Debug = $DebugValue
WhatIf = $WhatIfValue
ErrorAction = $STP
WarningAction = $CNT
}
$null = (Update-Module @paramUpdateModule)
}
catch
{
try
{
# Get error record
[Management.Automation.ErrorRecord]$e = $_
# retrieve information about runtime error
$info = [PSCustomObject]@{
Exception = $e.Exception.Message
Reason = $e.CategoryInfo.Reason
Target = $e.CategoryInfo.TargetName
Script = $e.InvocationInfo.ScriptName
Line = $e.InvocationInfo.ScriptLineNumber
Column = $e.InvocationInfo.OffsetInLine
}
# output information. Post-process collected info, and log info (optional)
$info | Out-String | Write-Verbose
Write-Verbose -Message ('Retry to update {0}' -f ($SingleModule).Name)
# Cleanup
$paramUpdateModule = $null
# Re-Try the update by allowing prereleases
$paramUpdateModule = @{
Name = (($SingleModule).Name)
AllowPrerelease = $true
Scope = $ModuleScope
Force = $true
AcceptLicense = $true
Confirm = $false
Verbose = $VerboseValue
Debug = $DebugValue
WhatIf = $WhatIfValue
ErrorAction = $STP
WarningAction = $CNT
}
$null = (Update-Module @paramUpdateModule)
}
catch
{
# Get error record
[Management.Automation.ErrorRecord]$e = $_
# retrieve information about runtime error
$info = [PSCustomObject]@{
Exception = $e.Exception.Message
Reason = $e.CategoryInfo.Reason
Target = $e.CategoryInfo.TargetName
Script = $e.InvocationInfo.ScriptName
Line = $e.InvocationInfo.ScriptLineNumber
Column = $e.InvocationInfo.OffsetInLine
}
# output information. Post-process collected info, and log info (optional)
$info | Out-String | Write-Verbose
Write-Warning -Message ('Update of {0} failed' -f ($SingleModule).Name)
}
}
}
else
{
Write-Verbose -Message ('No update for {0} found' -f ($SingleModule).Name)
}
}
}
end
{
if ($OriginalProgressPreference)
{
# Restore the old value
$ProgressPreference = $OriginalProgressPreference
}
# Cleanup
$AllModules = $null
# Have a great day!
}
#region LICENSE
<#
BSD 3-Clause License
Copyright (c) 2021, enabling Technology
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#>
#endregion LICENSE
#region DISCLAIMER
<#
DISCLAIMER:
- Use at your own risk, etc.
- This is open-source software, if you find an issue try to fix it yourself. There is no support and/or warranty in any kind
- This is a third-party Software
- The developer of this Software is NOT sponsored by or affiliated with Microsoft Corp (MSFT) or any of its subsidiaries in any way
- The Software is not supported by Microsoft Corp (MSFT)
- By using the Software, you agree to the License, Terms, and any Conditions declared and described above
- If you disagree with any of the terms, and any conditions declared: Just delete it and build your own solution
#>
#endregion DISCLAIMER

View File

@@ -0,0 +1,29 @@
BSD 3-Clause License
Copyright (c) 2021, enabling Technology <http://enatec.io>
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@@ -0,0 +1,311 @@
<#
.SYNOPSIS
Apply the Defender exclusions based on recommendations by Microsoft
.DESCRIPTION
Apply the Defender exclusions based on recommendations by Microsoft
.EXAMPLE
PS C:\> Optimize-MicrosoftDefenderExclusions.ps1
.NOTES
Do not just use set-mppreference here, this might remove any existing exclusions.
Might be the right thing to do, but with add-mppreference you append to the list (if exists).
.LINK
https://support.microsoft.com/en-ie/help/822158/virus-scanning-recommendations-for-enterprise-computers-that-are-runni
.LINK
https://docs.microsoft.com/en-us/powershell/module/defender/add-mppreference
.LINK
https://docs.microsoft.com/en-us/powershell/module/defender/set-mppreference
#>
[CmdletBinding(ConfirmImpact = 'Medium',
SupportsShouldProcess)]
param ()
begin
{
#region DefaultExclusions
$ExcludePathList = @(
"$env:windir\SoftwareDistribution\DataStore\Datastore.edb",
"$env:windir\SoftwareDistribution\DataStore\Logs\Edb*.jrs",
"$env:windir\SoftwareDistribution\DataStore\Logs\Edb.chk",
"$env:windir\SoftwareDistribution\DataStore\Logs\Tmp.edb",
"$env:windir\Security\Database\*.edb",
"$env:windir\Security\Database\*.sdb",
"$env:windir\Security\Database\*.log",
"$env:windir\Security\Database\*.chk",
"$env:windir\Security\Database\*.jrs",
"$env:windir\Security\Database\*.xml",
"$env:windir\Security\Database\*.csv",
"$env:windir\Security\Database\*.cmtx",
"$env:ProgramData\ntuser.pol",
"$env:windir\System32\GroupPolicy\Machine\Registry.pol",
"$env:windir\System32\GroupPolicy\Machine\Registry.tmp",
"$env:windir\System32\GroupPolicy\User\Registry.pol",
"$env:windir\System32\GroupPolicy\User\Registry.tmp"
)
#endregion DefaultExclusions
#region AdExclusions
# Turn off scanning of Active Directory and Active Directory-related files
# Exclude the Main NTDS database files.
$DSADatabaseFile = 'HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\NTDS\Parameters'
$DSADatabaseFilePath = ('Registry::' + $DSADatabaseFile)
if (Test-Path -Path $DSADatabaseFilePath)
{
$DSADatabaseFileValue = (Get-ItemProperty -Path $DSADatabaseFilePath | Select-Object -ExpandProperty 'DSA Database file' -ErrorAction SilentlyContinue)
if ($DSADatabaseFileValue)
{
$ExcludePathList += ($DSADatabaseFileValue)
$ExcludePathList += ($DSADatabaseFileValue).Replace('.dit', '.pat')
}
}
else
{
Write-Verbose -Message 'No NTDS database files to exclude'
}
# Exclude the Active Directory transaction log files.
$DatabaseLogFiles = 'HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\NTDS\Parameters'
$DatabaseLogFilesPath = ('Registry::' + $DatabaseLogFiles)
if (Test-Path -Path $DatabaseLogFilesPath)
{
$DatabaseLogFilesPathValue = (Get-ItemProperty -Path $DatabaseLogFilesPath | Select-Object -ExpandProperty 'Database Log Files Path' -ErrorAction SilentlyContinue)
if ($DatabaseLogFilesPathValue)
{
$ExcludePathList += ($DatabaseLogFilesPathValue + '\EDB*.log')
$ExcludePathList += ($DatabaseLogFilesPathValue + '\Res*.log')
$ExcludePathList += ($DatabaseLogFilesPathValue + '\Edb*.jrs')
$ExcludePathList += ($DatabaseLogFilesPathValue + '\Ntds.pat')
}
}
else
{
Write-Verbose -Message 'No Active Directory transaction log files to exclude'
}
# Exclude the files in the NTDS Working folder
$DSAWorkingDir = 'HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\NTDS\Parameters'
$DSAWorkingDirPath = ('Registry::' + $DSAWorkingDir)
if (Test-Path -Path $DSAWorkingDirPath)
{
$DSAWorkingDirValue = (Get-ItemProperty -Path $DSAWorkingDirPath | Select-Object -ExpandProperty 'DSA Working Directory' -ErrorAction SilentlyContinue)
if ($DSAWorkingDirValue)
{
$ExcludePathList += ($DSAWorkingDirValue + '\Temp.edb')
$ExcludePathList += ($DSAWorkingDirValue + '\Edb.chk')
}
}
else
{
Write-Verbose -Message 'No NTDS Working folder to exclude'
}
#endregion AdExclusions
#region SysVolExclusions
# Turn off scanning of SYSVOL files
# Turn off scanning of files in the File Replication Service (FRS) Working folder
$SysVolWorkingDir = 'HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\NtFrs\Parameters'
$SysVolWorkingDirPath = ('Registry::' + $SysVolWorkingDir)
if (Test-Path -Path $SysVolWorkingDirPath)
{
$SysVolWorkingDirValue = (Get-ItemProperty -Path $SysVolWorkingDirPath | Select-Object -ExpandProperty 'Working Directory' -ErrorAction SilentlyContinue)
if ($SysVolWorkingDirValue)
{
$ExcludePathList += ($SysVolWorkingDirValue + '\jet\sys\edb.chk')
$ExcludePathList += ($SysVolWorkingDirValue + '\jet\Ntfrs.jdb')
$ExcludePathList += ($SysVolWorkingDirValue + '\jet\log\*.log')
}
}
else
{
Write-Verbose -Message 'No File Replication Service Working folder to exclude'
}
# Turn off scanning of files in the File Replication Service Database Log files
$SysVolDBLogFileDir = 'HKEY_LOCAL_MACHINE\SYSTEM\Currentcontrolset\Services\Ntfrs\Parameters'
$SysVolDBLogFileDirPath = ('Registry::' + $SysVolDBLogFileDir)
if (Test-Path -Path $SysVolDBLogFileDirPath)
{
$SysVolDBLogFileDirValue = (Get-ItemProperty -Path $SysVolWorkingDirPath | Select-Object -ExpandProperty 'Working Directory' -ErrorAction SilentlyContinue)
if ($SysVolDBLogFileDirValue)
{
$ExcludePathList += ($SysVolDBLogFileDirValue + '\Jet\Log\Edb*.jrs')
}
else
{
if ($SysVolWorkingDirValue)
{
$ExcludePathList += ($SysVolWorkingDirValue + '\jet\Log\Edb*.log')
}
}
}
else
{
Write-Verbose -Message 'No File Replication Service Database Log files to exclude'
}
#endregion SysVolExclusions
#region DhcpExclusions
# Turn off scanning of DHCP files
$DhcpFiles = 'HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\DHCPServer\Parameters'
$DhcpFilesPath = ('Registry::' + $DhcpFiles)
if (Test-Path -Path $DhcpFilesPath)
{
$DhcpDatabasePathValue = (Get-ItemProperty -Path $DhcpFilesPath | Select-Object -ExpandProperty 'DatabasePath' -ErrorAction SilentlyContinue)
if ($DhcpDatabasePathValue)
{
$ExcludePathList += ($DhcpDatabasePathValue + '\*.mdb')
$ExcludePathList += ($DhcpDatabasePathValue + '\*.pat')
$ExcludePathList += ($DhcpDatabasePathValue + '\*.chk')
$ExcludePathList += ($DhcpDatabasePathValue + '\*.edb')
}
$DhcpLogFilePathValue = (Get-ItemProperty -Path $DhcpFilesPath | Select-Object -ExpandProperty 'DhcpLogFilePath' -ErrorAction SilentlyContinue)
if (($DhcpLogFilePathValue) -and ($DhcpLogFilePathValue -ne $DhcpDatabasePathValue))
{
$ExcludePathList += ($DhcpLogFilePathValue + '\*.log')
}
else
{
$ExcludePathList += ($DhcpDatabasePathValue + '\*.log')
}
$DhcpBackupDatabasePathValue = (Get-ItemProperty -Path $DhcpFilesPath | Select-Object -ExpandProperty 'BackupDatabasePath' -ErrorAction SilentlyContinue)
if ($DhcpBackupDatabasePathValue)
{
$ExcludePathList += ($DhcpBackupDatabasePathValue + '\new\*.mdb')
$ExcludePathList += ($DhcpBackupDatabasePathValue + '\new\*.pat')
$ExcludePathList += ($DhcpBackupDatabasePathValue + '\new\*.chk')
$ExcludePathList += ($DhcpBackupDatabasePathValue + '\new\*.edb')
$ExcludePathList += ($DhcpBackupDatabasePathValue + '\new\*.log')
}
}
else
{
Write-Verbose -Message 'No DHCP Server Directory found'
}
#endregion DhcpExclusions
#region DnsExclusions
$DnsServerDir = "$env:windir\System32\dns"
if (Test-Path -Path $DnsServerDir -ErrorAction SilentlyContinue)
{
$ExcludePathList += ($DnsServerDir + '\*.log')
$ExcludePathList += ($DnsServerDir + '\*.dns')
$ExcludePathList += ($DnsServerDir + '\BOOT')
$DnsBackupServerDir = ($DnsServerDir + '\backup')
if (Test-Path -Path $DnsBackupServerDir -ErrorAction SilentlyContinue)
{
$ExcludePathList += ($DnsBackupServerDir + '\*.log')
$ExcludePathList += ($DnsBackupServerDir + '\*.dns')
$ExcludePathList += ($DnsBackupServerDir + '\BOOT')
}
}
else
{
Write-Verbose -Message 'No DNS Server Directory found'
}
#endregion DnsExclusions
#region WinsExclusions
$WinsServerDir = "$env:windir\System32\Wins"
if (Test-Path -Path $WinsServerDir -ErrorAction SilentlyContinue)
{
Write-Warning -Message 'WINS is still installed on this system!' -WarningAction Continue
$ExcludePathList += ($WinsServerDir + '\*.chk')
$ExcludePathList += ($WinsServerDir + '\*.log')
$ExcludePathList += ($WinsServerDir + '\*.mdb')
}
else
{
Write-Verbose -Message 'No WINS Server Directory found'
}
#endregion WinsExclusions
}
process
{
if ($pscmdlet.ShouldProcess($ExcludePathList, 'Exclude from Microsoft Defender Scanning'))
{
# Loop over the list we created
foreach ($ExcludePath in $ExcludePathList)
{
try
{
# Splat the parameters for Add-MpPreference
$SplatAddMpPreference = @{
ExclusionPath = $ExcludePath
Force = $true
ErrorAction = 'Stop'
WarningAction = 'Continue'
}
$null = (Add-MpPreference @SplatAddMpPreference)
}
catch
{
#region ErrorHandler
# get error record
[Management.Automation.ErrorRecord]$e = $_
# retrieve information about runtime error
$info = @{
Exception = $e.Exception.Message
Reason = $e.CategoryInfo.Reason
Target = $e.CategoryInfo.TargetName
Script = $e.InvocationInfo.ScriptName
Line = $e.InvocationInfo.ScriptLineNumber
Column = $e.InvocationInfo.OffsetInLine
}
# Error Stack
$info | Out-String | Write-Verbose
# Just display the info on continue with the rest of the list
Write-Warning -Message ($info.Exception) -ErrorAction Continue -WarningAction Continue
# Cleanup
$info = $null
$e = $null
#endregion ErrorHandler
}
}
}
}
#region LICENSE
<#
BSD 3-Clause License
Copyright (c) 2021, enabling Technology
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#>
#endregion LICENSE
#region DISCLAIMER
<#
DISCLAIMER:
- Use at your own risk, etc.
- This is open-source software, if you find an issue try to fix it yourself. There is no support and/or warranty in any kind
- This is a third-party Software
- The developer of this Software is NOT sponsored by or affiliated with Microsoft Corp (MSFT) or any of its subsidiaries in any way
- The Software is not supported by Microsoft Corp (MSFT)
- By using the Software, you agree to the License, Terms, and any Conditions declared and described above
- If you disagree with any of the terms, and any conditions declared: Just delete it and build your own solution
#>
#endregion DISCLAIMER

View File

@@ -0,0 +1,133 @@
<#
.SYNOPSIS
Add Defender Antivirus Exclusions for the Teams Desktop Client for a given User
.DESCRIPTION
Add Defender Antivirus Exclusions for the Teams Desktop Client for a given User
.PARAMETER Username
Username to apply the exclusion to.
Please Note: The user 'john.doe' in the domain 'CONTOSO' will have the username 'john.doe.CONTOSO'. This is the case to have the connect Directory (Windows naming convention).
.EXAMPLE
PS C:\> .\Optimize-MicrosoftDefenderExclusionsForMicrosoftTeams.ps1 -Username 'john.doe.CONTOSO'
Apply the Defender Antivirus Exclusions for the user 'john.doe' in the domain 'CONTOSO'.
In this case, the $env:USERPROFILE Directory will be 'C:\Users\john.doe.CONTOSO'
.NOTES
This is a more flexible version of Add-DefenderExclusionsForMicrosoftteams.ps1 that brings username as a parameter.
I crerated this because my user does NOT have Admin permissions on my local windows boxes and with this version, I can apply it with my admin account, biut for my regular user (or any other user on the local system)
Do not just use set-mppreference here, this might remove any existing exclusions.
Might be the right thing to do, but with add-mppreference you append to the list (if exists).
.LINK
https://gist.github.com/jhochwald/866ce1c5ac894397979f38fa9720b8ff
.LINK
https://docs.microsoft.com/en-us/powershell/module/defender/add-mppreference
.LINK
https://docs.microsoft.com/en-us/powershell/module/defender/set-mppreference
#>
[CmdletBinding(ConfirmImpact = 'Low',
SupportsShouldProcess)]
param
(
[Parameter(Mandatory,
ValueFromPipeline,
ValueFromPipelineByPropertyName,
Position = 0,
HelpMessage = 'Username to apply the exclusion to.')]
[ValidateNotNullOrEmpty()]
[Alias('User', 'Name')]
[string]
$Username
)
begin
{
$ExcludePathList = @(
('C:\Users\' + $Username + '\Microsoft\Teams\Update.exe'),
('C:\Users\' + $Username + '\Microsoft\Teams\current\Teams.exe'),
('C:\Users\' + $Username + '\Microsoft\Teams\'),
('C:\Users\' + $Username + '\Microsoft\Teams\')
)
}
process
{
# Loop over the list we created
foreach ($ExcludePath in $ExcludePathList)
{
try
{
# Splat the parameters for Add-MpPreference
$SplatAddMpPreference = @{
ExclusionPath = $ExcludePath
Force = $true
ErrorAction = 'Stop'
WarningAction = 'Continue'
}
$null = (Add-MpPreference @SplatAddMpPreference)
}
catch
{
#region ErrorHandler
# get error record
[Management.Automation.ErrorRecord]$e = $_
# retrieve information about runtime error
$info = @{
Exception = $e.Exception.Message
Reason = $e.CategoryInfo.Reason
Target = $e.CategoryInfo.TargetName
Script = $e.InvocationInfo.ScriptName
Line = $e.InvocationInfo.ScriptLineNumber
Column = $e.InvocationInfo.OffsetInLine
}
# Error Stack
$info | Out-String | Write-Verbose
# Just display the info on continue with the rest of the list
Write-Warning -Message ($info.Exception) -ErrorAction Continue -WarningAction Continue
# Cleanup
$info = $null
$e = $null
#endregion ErrorHandler
}
}
}
#region LICENSE
<#
BSD 3-Clause License
Copyright (c) 2021, enabling Technology
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#>
#endregion LICENSE
#region DISCLAIMER
<#
DISCLAIMER:
- Use at your own risk, etc.
- This is open-source software, if you find an issue try to fix it yourself. There is no support and/or warranty in any kind
- This is a third-party Software
- The developer of this Software is NOT sponsored by or affiliated with Microsoft Corp (MSFT) or any of its subsidiaries in any way
- The Software is not supported by Microsoft Corp (MSFT)
- By using the Software, you agree to the License, Terms, and any Conditions declared and described above
- If you disagree with any of the terms, and any conditions declared: Just delete it and build your own solution
#>
#endregion DISCLAIMER

View File

@@ -0,0 +1,229 @@
function Out-ZipArchive
{
<#
.SYNOPSIS
Creates a ZIP Archive
.DESCRIPTION
Creates a ZIP Archive with all given Files (and subdirectories)
.PARAMETER Path
Input Path
.PARAMETER ArchiveName
Name of the archive to create.
.PARAMETER force
Enforce overwrite?
.PARAMETER fallback
Use Microsoft .NET Framework API instead of Compress-Archive (Bundled with PowerShell 5.0, or later)
.EXAMPLE
PS C:\> Out-ZipArchive -Path 'Value1' -ArchiveName 'Value2'
Creates a ZIP Archive with all given Files (and subdirectories)
.EXAMPLE
PS C:\> Out-ZipArchive -Path 'Value1' -ArchiveName 'Value2' -fallback
Creates a ZIP Archive with all given Files (and subdirectories) - Use .NET Framework API instead of Compress-Archive internal
.NOTES
We now use Compress-Archive by default. It is build upon the Microsoft .NET Framework API System.IO.Compression.ZipArchive and has the same limitation.
.LINK
Compress-Archive
.LINK
https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.archive/compress-archive
#>
[CmdletBinding(ConfirmImpact = 'None')]
param
(
[Parameter(Mandatory = $true,
ValueFromPipeline = $true,
ValueFromPipelineByPropertyName = $true,
Position = 0,
HelpMessage = 'Input Path')]
[ValidateNotNullOrEmpty()]
[Alias('Directory')]
[string]
$Path,
[Parameter(Mandatory = $true,
ValueFromPipeline = $true,
ValueFromPipelineByPropertyName = $true,
Position = 1,
HelpMessage = 'Name of the archive to create')]
[ValidateNotNullOrEmpty()]
[Alias('FileName')]
[string]
$ArchiveName,
[Parameter(ValueFromPipeline = $true,
ValueFromPipelineByPropertyName = $true,
Position = 2)]
[Alias('overwrite')]
[switch]
$force,
[Parameter(ValueFromPipeline = $true,
ValueFromPipelineByPropertyName = $true,
Position = 3)]
[Alias('dotnet')]
[switch]
$fallback = $false
)
begin
{
$null = (Add-Type -AssemblyName System.IO.Compression.FileSystem)
$compressionLevel = [IO.Compression.CompressionLevel]::Optimal
Write-Verbose -Message "Compression level for $ArchiveName is $compressionLevel"
# Safe ProgressPreference and Setup SilentlyContinue for the function
$ExistingProgressPreference = ($ProgressPreference)
$ProgressPreference = 'SilentlyContinue'
}
process
{
if (-not $ArchiveName.EndsWith('.zip'))
{
Write-Verbose -Message "Bad filename detected $ArchiveName"
$ArchiveName += '.zip'
Write-Verbose -Message "Corrected filename is $ArchiveName"
}
if ($force)
{
if (Test-Path -Path $ArchiveName -ErrorAction SilentlyContinue -WarningAction SilentlyContinue)
{
Write-Verbose -Message "Overwrite old archive $ArchiveName"
try
{
$paramRemoveItem = @{
Path = $ArchiveName
Force = $true
Confirm = $false
ErrorAction = 'Stop'
WarningAction = 'Continue'
}
$null = (Remove-Item @paramRemoveItem)
}
catch
{
# Get error record
[Management.Automation.ErrorRecord]$e = $_
# Retrieve information about runtime error
$info = [PSCustomObject]@{
Exception = $e.Exception.Message
Reason = $e.CategoryInfo.Reason
Target = $e.CategoryInfo.TargetName
Script = $e.InvocationInfo.ScriptName
Line = $e.InvocationInfo.ScriptLineNumber
Column = $e.InvocationInfo.OffsetInLine
}
# Output information. Post-process collected info, and log info (optional)
$info | Out-String | Write-Verbose
Write-Error -Message ($info.Exception) -TargetObject ($info.Target) -ErrorAction Stop
break
}
}
}
try
{
Write-Verbose -Message "Try to create archive $ArchiveName"
if ($fallback)
{
Write-Verbose -Message 'Run in fallback mode and using System.IO.Compression.ZipArchive instead of Compress-Archive'
$zip = ([IO.Compression.ZipFile]::CreateFromDirectory($Path, $ArchiveName, $compressionLevel, $false))
# And always make sure to close the locks on that file
$zip.Dispose()
}
else
{
$paramCompressArchive = @{
Path = $Path
CompressionLevel = $compressionLevel
DestinationPath = $ArchiveName
Force = $true
Confirm = $false
ErrorAction = 'Stop'
WarningAction = 'SilentlyContinue'
}
$null = (Compress-Archive @paramCompressArchive)
}
Write-Verbose -Message "Archive $ArchiveName was created"
}
catch
{
# Get error record
[Management.Automation.ErrorRecord]$e = $_
# Retrieve information about runtime error
$info = [PSCustomObject]@{
Exception = $e.Exception.Message
Reason = $e.CategoryInfo.Reason
Target = $e.CategoryInfo.TargetName
Script = $e.InvocationInfo.ScriptName
Line = $e.InvocationInfo.ScriptLineNumber
Column = $e.InvocationInfo.OffsetInLine
}
# Output information. Post-process collected info, and log info (optional)
$info | Out-String | Write-Verbose
Write-Error -Message ($info.Exception) -TargetObject ($info.Target) -ErrorAction Stop
break
}
}
end
{
# Restore ProgressPreference
$ProgressPreference = $ExistingProgressPreference
Write-Verbose -Message 'Out-ZipArchive done'
}
}
#region LICENSE
<#
BSD 3-Clause License
Copyright (c) 2021, enabling Technology
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#>
#endregion LICENSE
#region DISCLAIMER
<#
DISCLAIMER:
- Use at your own risk, etc.
- This is open-source software, if you find an issue try to fix it yourself. There is no support and/or warranty in any kind
- This is a third-party Software
- The developer of this Software is NOT sponsored by or affiliated with Microsoft Corp (MSFT) or any of its subsidiaries in any way
- The Software is not supported by Microsoft Corp (MSFT)
- By using the Software, you agree to the License, Terms, and any Conditions declared and described above
- If you disagree with any of the terms, and any conditions declared: Just delete it and build your own solution
#>
#endregion DISCLAIMER

View File

@@ -0,0 +1,245 @@
function Publish-BitbucketDownload
{
<#
.SYNOPSIS
Upload given file to BitBucket cloud service downloads section.
.DESCRIPTION
Upload given file to BitBucket cloud service downloads section.
I use this to upload build artifacts to the BitBucket Download section.
The code might not be perfect, and we still use the AUTH Header instead of OAuth yet,
but I needed a quick and dirty solution to get things going.
I might change a few things soon, but for now; this function is doing what it should.
.PARAMETER username
BitBucket cloud username, as plain text
.PARAMETER password
BitBucket cloud password, as plain text
.PARAMETER FilePath
File to upload, full path needed
.PARAMETER team
BitBucket cloud team aka username (Might not be the login username!!!)
.PARAMETER Project
BitBucket cloud project name
.EXAMPLE
PS ~> Publish-BitbucketDownload -username 'MyUsername' -password 'MySectretPassword' -FilePath 'Y:\dev\release\myproject-current.zip' -team 'dummyTeam' -Project 'myproject'
# Upload the artifact 'Y:\dev\release\myproject-current.zip' to the Download sections of the 'myproject' project of the 'dummyTeam', It uses User name and password (Both in plain ASC) to authenticate. However, both are converted to base64 to prevent any clear text header transfers.
.EXAMPLE
PS ~> Publish-BitbucketDownload -username 'MyUsername' -password 'MySectretPassword' -FilePath 'Y:\dev\release\myproject.nuget' -team 'dummyTeam' -Project 'myproject'
# Upload the artifact 'Y:\dev\release\myproject.nuget' to the Download sections of the 'myproject' project of the 'dummyTeam', It uses Username and password (Both in plain ASC) to authenticate. However, both are converted to base64 to prevent any clear text header transfers.
.NOTES
I created this because I did not have CURL installed on my build system.
With Curl this is an absolute no brainer:
curl -X POST "https://MyUsername:MySectretPassword@api.bitbucket.org/2.0/repositories/dummyTeam/myproject/downloads" --form files=@"/home/dev/release\myproject-current.zip"
INFO: Max. CPU: 16 % Max. Memory: 28.48 MB
TODO: Convert the request to use OAuth ASAP
#>
[CmdletBinding()]
param
(
[Parameter(Mandatory,
ValueFromPipeline,
HelpMessage = 'BitBucket cloud username, as plain text')]
[ValidateNotNullOrEmpty()]
[Alias('user')]
[string]
$username,
[Parameter(Mandatory,
ValueFromPipeline,
HelpMessage = 'BitBucket cloud password, as plain text')]
[ValidateNotNullOrEmpty()]
[Alias('pass')]
[string]
$password,
[Parameter(Mandatory,
ValueFromPipeline,
HelpMessage = 'File to upload, full path needed')]
[ValidateNotNullOrEmpty()]
[string]
$FilePath,
[Parameter(Mandatory,
ValueFromPipeline,
HelpMessage = 'BitBucket cloud team name')]
[ValidateNotNullOrEmpty()]
[string]
$team,
[Parameter(Mandatory,
ValueFromPipeline,
HelpMessage = 'BitBucket cloud project name')]
[ValidateNotNullOrEmpty()]
[Alias('ProjectName')]
[string]
$Project
)
process
{
# Build the URI for our request
$URI = 'https://api.bitbucket.org/2.0/repositories/' + $team + '/' + $Project + '/downloads'
# Create our authentication header
# TODO: Migrate to OAUTH
$pair = ($username + ':' + $password)
$encodedCreds = [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes($pair))
$basicAuthValue = ('Basic {0}' -f $encodedCreds)
$Headers = @{
Authorization = $basicAuthValue
}
# Cleanup the plain text stuff
$pair = $null
$encodedCreds = $null
# The boundary is essential - Trust me, very essential
$boundary = [Guid]::NewGuid().ToString()
<#
This is the crappy part: Build a body for a multipart request with PowerShell
This is something that should be changed in PowerShell ASAP (I mean it is really crappy and really bad).
It is an absolute no brainer with Curl.
#>
$bodyStart = @"
--$boundary
Content-Disposition: form-data; name="token"
--$boundary
Content-Disposition: form-data; name="files"; filename="$(Split-Path -Leaf -Path $FilePath)"
Content-Type: application/octet-stream
"@
# Generate the end of the request body to finish it.
$bodyEnd = @"
--$boundary--
"@
# Now we create a temp file (Another crappy/bad thing)
$requestInFile = (Join-Path -Path $env:TEMP -ChildPath ([IO.Path]::GetRandomFileName()))
try
{
# Create a new object for the brand new temporary file
$fileStream = (New-Object -TypeName 'System.IO.FileStream' -ArgumentList ($requestInFile, [IO.FileMode]'Create', [IO.FileAccess]'Write'))
try
{
# The Body start
$bytes = [Text.Encoding]::UTF8.GetBytes($bodyStart)
$fileStream.Write($bytes, 0, $bytes.Length)
# The original File
$bytes = [IO.File]::ReadAllBytes($FilePath)
$fileStream.Write($bytes, 0, $bytes.Length)
# Append the end of the body part
$bytes = [Text.Encoding]::UTF8.GetBytes($bodyEnd)
$fileStream.Write($bytes, 0, $bytes.Length)
}
finally
{
# End the Stream to close the file
$fileStream.Close()
# Cleanup
$fileStream = $null
# PowerShell garbage collector
[GC]::Collect()
}
# Make it multipart, this is the magic part...
$contentType = 'multipart/form-data; boundary={0}' -f $boundary
<#
The request itself is simple and easy, also works fine with Invoke-WebRequest instead of Invoke-RestMethod
I use Microsoft.PowerShell.Utility\Invoke-RestMethod to make sure the build in (Windows PowerShell native) function is used.
If PowerShell Core is installed or any Module provides a tweaked version... Just in case!
#>
try
{
$null = (Microsoft.PowerShell.Utility\Invoke-RestMethod -Uri $URI -Method Post -InFile $requestInFile -ContentType $contentType -Headers $Headers -ErrorAction Stop -WarningAction SilentlyContinue)
}
catch
{
# Remove the temp file
$null = (Remove-Item -Path $requestInFile -Force -Confirm:$false)
# Cleanup
$contentType = $null
# PowerShell garbage collector
[GC]::Collect()
# For the Build logs (will not break the build)
Write-Warning -Message 'StatusCode:' $_.Exception.Response.StatusCode.value__
Write-Warning -Message 'StatusDescription:' $_.Exception.Response.StatusDescription
# Saved in the verbose logs for this build
Write-Verbose -Message $_
# Inform the build and terminate (Will break the build)
Write-Error -Message 'We were unable to upload your file to the BitBucket downloads section, please check the build logs for further information.' -ErrorAction Stop
}
}
finally
{
# Remove the temp file
$null = (Remove-Item -Path $requestInFile -Force -Confirm:$false)
# Cleanup
$contentType = $null
# PowerShell garbage collector
[GC]::Collect()
}
}
}
#region LICENSE
<#
BSD 3-Clause License
Copyright (c) 2021, enabling Technology
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#>
#endregion LICENSE
#region DISCLAIMER
<#
DISCLAIMER:
- Use at your own risk, etc.
- This is open-source software, if you find an issue try to fix it yourself. There is no support and/or warranty in any kind
- This is a third-party Software
- The developer of this Software is NOT sponsored by or affiliated with Microsoft Corp (MSFT) or any of its subsidiaries in any way
- The Software is not supported by Microsoft Corp (MSFT)
- By using the Software, you agree to the License, Terms, and any Conditions declared and described above
- If you disagree with any of the terms, and any conditions declared: Just delete it and build your own solution
#>
#endregion DISCLAIMER

View File

@@ -0,0 +1,211 @@
function Remove-FileEndingBlankLines
{
<#
.SYNOPSIS
Strip white space/blank lines from end of file or path
.DESCRIPTION
Strip white space/blank lines from end of file or path
.PARAMETER Path
Single File or Path you want to unclutter. (Mandatory)
.PARAMETER Recurse
Recurse through all subdirectories of the path provided. The default is not work recursively (Optional)
.PARAMETER noNewLine
No new (blank) line at the end of a file.
.PARAMETER SafeFilesOnly
Only safe files were processed. This is the default! This will prevent any issues with Binary Files or any other non safe to process files. If you like to process all files (can be dangerous) just negate this by using -SafeFilesOnly:$false
.EXAMPLE
PS C:\> Remove-FileEndingBlankLines -Path 'C:\Temp\Export-DistributionGroup2Cloud.ps1'
Strip white space/blank lines from end of 'C:\Temp\Export-DistributionGroup2Cloud.ps1'
.EXAMPLE
PS C:\> Remove-FileEndingBlankLines -Path 'C:\Temp\Export-DistributionGroup2Cloud.ps1'
Strip white space/blank lines from end of 'C:\Temp\Export-DistributionGroup2Cloud.ps1' without ending a final blank line at the end.
NOTE: Set-Content adds a final blank line by default. this switch prevents this!
.EXAMPLE
PS C:\> Remove-FileEndingBlankLines -Path 'C:\Temp' -Recurse
Strip white space/blank lines from end of files found in 'C:\Temp' and below (recursively).
.EXAMPLE
PS C:\> Remove-FileEndingBlankLines -Path 'C:\Temp' -Recurse -SafeFilesOnly:$false
Strip white space/blank lines from end of all files found in 'C:\Temp' and below (recursively).
This might be risky and/or even dangerous! If you process any binary files, they might be corrupt afterwards.
.NOTES
I created this helper function to unclutter the file ends and white space/blank lines from files during my build process.
I prefer the way that Set-Content handles it: Add a single blank line at the end of each file. This is use to the fact, that I concatenate several files during a build process.
I also added a switch (noNewLine) to prevent this.
By default only PowerShell and Markdown Files are processed by this function
.LINK
Set-Content
.LINK
Get-Content
#>
[CmdletBinding(ConfirmImpact = 'Low',
SupportsShouldProcess)]
param
(
[Parameter(Mandatory,
ValueFromPipeline,
ValueFromPipelineByPropertyName,
Position = 1,
HelpMessage = 'Single File or Path you want to unclutter.')]
[ValidateNotNullOrEmpty()]
[Alias('FilePath')]
[string]
$Path,
[Parameter(ValueFromPipeline,
ValueFromPipelineByPropertyName,
Position = 2)]
[switch]
$Recurse = $false,
[Parameter(ValueFromPipeline,
ValueFromPipelineByPropertyName,
Position = 3)]
[switch]
$noNewLine = $false,
[Parameter(ValueFromPipeline,
ValueFromPipelineByPropertyName,
Position = 4)]
[switch]
$SafeFilesOnly = $true
)
begin
{
$paramGetChildItem = @{
Path = $Path
File = $true
}
if ($SafeFilesOnly)
{
Write-Verbose -Message 'Only safe files are processed'
$paramGetChildItem.Include = '*.psm1', '*.ps1', '*.psd1', '*.ps1xml', '*.md'
}
else
{
Write-Verbose -Message 'All are processed - Might be a bad idea!!!'
}
if ($Recurse)
{
Write-Verbose -Message 'Read the info recursively'
$paramGetChildItem.Recurse = $true
}
else
{
Write-Verbose -Message 'Read the info'
}
}
process
{
# Make sure only files are processed and get the minimal info
(Get-ChildItem @paramGetChildItem | Where-Object -FilterScript {
-not $_.PSIsContainer
} | Select-Object -ExpandProperty FullName) | ForEach-Object -Process {
Write-Verbose -Message ('Try to unclutter {0}' -f $_)
$UnclutteredText = (((Get-Content -Path $_ -Raw).TrimEnd()).ToString())
try
{
if ($noNewLine)
{
Write-Verbose -Message ('Try to unclutter {0} (no final new line)' -f $_)
$null = ([io.file]::WriteAllText($_.FullName, $UnclutteredText))
}
else
{
Write-Verbose -Message ('Try to unclutter {0}' -f $_)
$paramSetContent = @{
Path = $_
Value = $UnclutteredText
Force = $true
Confirm = $false
Encoding = 'UTF8'
ErrorAction = 'Stop'
WarningAction = 'Continue'
}
$null = (Set-Content @paramSetContent)
}
Write-Verbose -Message ('Uncluttered {0}' -f $_)
}
catch
{
# Get error record
[Management.Automation.ErrorRecord]$e = $_
# Retrieve information about runtime error
$info = [PSCustomObject]@{
Exception = $e.Exception.Message
Reason = $e.CategoryInfo.Reason
Target = $e.CategoryInfo.TargetName
Script = $e.InvocationInfo.ScriptName
Line = $e.InvocationInfo.ScriptLineNumber
Column = $e.InvocationInfo.OffsetInLine
}
# Output information. Post-process collected info, and log info (optional)
$info | Out-String | Write-Verbose
Write-Error -Message ($info.Exception) -TargetObject ($info.Target) -ErrorAction Stop
break
}
}
}
end
{
Write-Verbose -Message 'Clear-FileEnding Done'
}
}
#region LICENSE
<#
BSD 3-Clause License
Copyright (c) 2021, enabling Technology
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#>
#endregion LICENSE
#region DISCLAIMER
<#
DISCLAIMER:
- Use at your own risk, etc.
- This is open-source software, if you find an issue try to fix it yourself. There is no support and/or warranty in any kind
- This is a third-party Software
- The developer of this Software is NOT sponsored by or affiliated with Microsoft Corp (MSFT) or any of its subsidiaries in any way
- The Software is not supported by Microsoft Corp (MSFT)
- By using the Software, you agree to the License, Terms, and any Conditions declared and described above
- If you disagree with any of the terms, and any conditions declared: Just delete it and build your own solution
#>
#endregion DISCLAIMER

View File

@@ -0,0 +1,188 @@
function Remove-Signature
{
<#
.SYNOPSIS
Finds all signed PowerShell files removes any digital signatures attached to them.
.DESCRIPTION
Finds all signed PowerShell files removes any digital signatures attached to them.
Supported Filetypes are: psm1, ps1, psd1, and ps1xml - All other Files are ignored!
.PARAMETER Path
Single File or Path you want to parse for digital signatures. (Mandatory)
.PARAMETER Recurse
Recurse through all subdirectories of the path provided. The default is not work recursively (Optional)
.EXAMPLE
PS C:\> Remove-Signature -Path 'C:\Temp\Export-DistributionGroup2Cloud.ps1'
Removes all digital signatures from 'C:\Temp\Export-DistributionGroup2Cloud.ps1'
.EXAMPLE
PS C:\> Remove-Signature -Path 'C:\Temp' -Recurse
Removes all digital signatures from psm1, ps1, psd1, and ps1xml files found in 'C:\Temp' and below (recursively).
.NOTES
Based on the ideas and work of the original Authors: Adrian Rodriguez and Zachary Loeber
.LINK
http://www.the-little-things.net
.LINK
https://psrdrgz.github.io/RemoveAuthenticodeSignature/
#>
[CmdletBinding(ConfirmImpact = 'Low',
SupportsShouldProcess)]
param
(
[Parameter(Mandatory,
ValueFromPipeline,
ValueFromPipelineByPropertyName,
Position = 1,
HelpMessage = 'Single File or Path you want to parse for digital signatures.')]
[ValidateNotNullOrEmpty()]
[Alias('FilePath')]
[string]
$Path,
[Parameter(ValueFromPipeline,
ValueFromPipelineByPropertyName,
Position = 2)]
[switch]
$Recurse = $false
)
begin
{
$paramGetChildItem = @{
Path = $Path
File = $true
Include = '*.psm1', '*.ps1', '*.psd1', '*.ps1xml'
}
if ($Recurse)
{
Write-Verbose -Message 'Work recursively'
$paramGetChildItem.Recurse = $true
}
}
process
{
$FilesToProcess = (Get-ChildItem @paramGetChildItem)
$FilesToProcess | ForEach-Object -Process {
$SignatureStatus = (Get-AuthenticodeSignature -FilePath $_).Status
$ScriptFileFullName = $_.FullName
if ($SignatureStatus -ne 'NotSigned')
{
try
{
$paramGetContent = @{
Path = $ScriptFileFullName
ErrorAction = 'Stop'
WarningAction = 'Continue'
}
$Content = (Get-Content @paramGetContent)
$paramNewObject = @{
TypeName = 'System.Text.StringBuilder'
ErrorAction = 'Stop'
WarningAction = 'Continue'
}
$StringBuilder = (New-Object @paramNewObject)
foreach ($Line in $Content)
{
if ($Line -match '^# SIG # Begin signature block|^<!-- SIG # Begin signature block -->')
{
break
}
else
{
$null = $StringBuilder.AppendLine($Line)
}
}
if ($pscmdlet.ShouldProcess("$ScriptFileFullName"))
{
$paramSetContent = @{
Path = $ScriptFileFullName
Value = $StringBuilder.ToString()
Force = $true
Confirm = $false
Encoding = 'UTF8'
ErrorAction = 'Stop'
WarningAction = 'Continue'
}
$null = (Set-Content @paramSetContent)
Write-Verbose -Message ('Removed signature from {0}' -f $ScriptFileFullName)
}
}
catch
{
# Get error record
[Management.Automation.ErrorRecord]$e = $_
# Retrieve information about runtime error
$info = [PSCustomObject]@{
Exception = $e.Exception.Message
Reason = $e.CategoryInfo.Reason
Target = $e.CategoryInfo.TargetName
Script = $e.InvocationInfo.ScriptName
Line = $e.InvocationInfo.ScriptLineNumber
Column = $e.InvocationInfo.OffsetInLine
}
# Output information. Post-process collected info, and log info (optional)
$info | Out-String | Write-Verbose
Write-Error -Message ($info.Exception) -TargetObject ($info.Target) -ErrorAction Stop
break
}
}
else
{
Write-Verbose -Message ('No signature found in {0}' -f $ScriptFileFullName)
}
}
}
end
{
Write-Verbose -Message 'Remove-Signature Done'
}
}
#region LICENSE
<#
BSD 3-Clause License
Copyright (c) 2021, enabling Technology
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#>
#endregion LICENSE
#region DISCLAIMER
<#
DISCLAIMER:
- Use at your own risk, etc.
- This is open-source software, if you find an issue try to fix it yourself. There is no support and/or warranty in any kind
- This is a third-party Software
- The developer of this Software is NOT sponsored by or affiliated with Microsoft Corp (MSFT) or any of its subsidiaries in any way
- The Software is not supported by Microsoft Corp (MSFT)
- By using the Software, you agree to the License, Terms, and any Conditions declared and described above
- If you disagree with any of the terms, and any conditions declared: Just delete it and build your own solution
#>
#endregion DISCLAIMER

View File

@@ -0,0 +1,118 @@
function Resolve-DNSHost
{
<#
.SYNOPSIS
Resolve DNS hostname to IP and reverse
.DESCRIPTION
This function resolves DNS hostname to IP and the other way around (reverse)
.PARAMETER HostEntry
Hostname (Single, or multiple) to test.
.EXAMPLE
PS C:\> Resolve-DNSHost -HostEntry www.hochwald.net
HostName IPAddress
-------- ---------
www.hochwald.net {104.28.0.64, 104.28.1.64, 2606:4700:30::681c:140, 2606:4700:30::681c:40}
This function resolves DNS hostname to IP and the other way around (reverse)
.EXAMPLE
PS C:\> Resolve-DNSHost -HostEntry 'www.hochwald.net','autodiscover.hochwald.net'
HostName IPAddress
-------- ---------
www.hochwald.net {104.28.0.64, 104.28.1.64, 2606:4700:30::681c:140, 2606:4700:30::681c:40}
autodiscover.hochwald.net {40.101.88.8, 40.101.88.184, 52.97.151.104, 40.101.60.24...}
This function resolves DNS hostname to IP and the other way around (reverse)
.OUTPUTS
psobject
.NOTES
Refactored of Resolve-Host.Ps1 by @PrateekKumarSingh
.LINK
Original:
https://gist.github.com/PrateekKumarSingh/586f2d3d43f7e8cb07ce
.LINK
Dns Class (system.net.dns):
https://docs.microsoft.com/de-de/dotnet/api/system.net.dns
.INPUTS
String
#>
[CmdletBinding(ConfirmImpact = 'None')]
[OutputType([psobject])]
param
(
[Parameter(Mandatory,
ValueFromPipeline,
ValueFromPipelineByPropertyName,
Position = 0,
HelpMessage = 'Hostname (Single, or multiple) to test.')]
[ValidateNotNullOrEmpty()]
[String[]]
$HostEntry
)
begin
{
# Cleanup
$Obj = @()
$Object = @()
}
process
{
$HostEntry | ForEach-Object -Process {
$Obj += New-Object -TypeName psobject -Property @{
HostName = $_
IPAddress = $([Net.Dns]::gethostentry($_).AddressList.IPAddressToString)
}
}
# Append
$Object = ($Obj | Select-Object -Property Hostname, IPAddress)
}
end
{
# Dump to the console
$Object
}
}
#region LICENSE
<#
BSD 3-Clause License
Copyright (c) 2021, enabling Technology
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#>
#endregion LICENSE
#region DISCLAIMER
<#
DISCLAIMER:
- Use at your own risk, etc.
- This is open-source software, if you find an issue try to fix it yourself. There is no support and/or warranty in any kind
- This is a third-party Software
- The developer of this Software is NOT sponsored by or affiliated with Microsoft Corp (MSFT) or any of its subsidiaries in any way
- The Software is not supported by Microsoft Corp (MSFT)
- By using the Software, you agree to the License, Terms, and any Conditions declared and described above
- If you disagree with any of the terms, and any conditions declared: Just delete it and build your own solution
#>
#endregion DISCLAIMER

View File

@@ -0,0 +1,133 @@
#requires -Version 3.0 -Modules NetSecurity -RunAsAdministrator
<#
.SYNOPSIS
Enable inbound ICMP (Ping) and Remote Desktop (RDP)
.DESCRIPTION
Enable inbound ICMP (Ping) and Remote Desktop (RDP).
Ping will be enabled for IPv4 and IPv6.
.PARAMETER RDPGroup
Enable the complete RDP Groups in the Windows Firewall?
This will enable more then just the basic requirements, use with care!!!
.EXAMPLE
PS C:\> .\Set-AllowPingAndRemoteDesktop.ps1
Enable inbound ICMP (Ping) and Remote Desktop (RDP)
.EXAMPLE
PS C:\> .\Set-AllowPingAndRemoteDesktop.ps1 -verbose
Enable inbound ICMP (Ping) and Remote Desktop (RDP) - verbose run
.EXAMPLE
PS C:\> .\Set-AllowPingAndRemoteDesktop.ps1 -WhatIf
Enable inbound ICMP (Ping) and Remote Desktop (RDP) - Dry run
.NOTES
Helper script I use to bootstrap servers
Run this elevated!!!
#>
[CmdletBinding(ConfirmImpact = 'Medium',
SupportsShouldProcess)]
param
(
[Parameter(ValueFromPipeline)]
[switch]
$RDPGroup
)
begin
{
# Splat the Set-ItemProperty parameters
$paramSetItemProperty = @{
Path = 'HKLM:\System\CurrentControlSet\Control\Terminal Server'
Name = 'fDenyTSConnections'
Value = 0
ErrorAction = 'Continue'
}
# Splat the Enable-NetFirewallRule parameters
$paramEnableNetFirewallRule = @{
Confirm = $false
ErrorAction = 'Continue'
}
}
process
{
# Support WhatIf (SupportsShouldProcess)
if ($pscmdlet.ShouldProcess('Registry Terminal Server', 'Modify'))
{
# Tweak the Registry for Remote Desktop connections
$null = (Set-ItemProperty @paramSetItemProperty)
}
# We avoid using $RDPGroup.IsPresent
if ($PSBoundParameters.ContainsKey('RDPGroup'))
{
if ($pscmdlet.ShouldProcess('Firewall Group for Remote Desktop', 'Enable'))
{
# Allow Remote Desktop (The Group)
$null = (Get-NetFirewallRule -DisplayGroup 'Remote Desktop' -ErrorAction SilentlyContinue | Where-Object {
$_.Enabled -ne $true
} | Enable-NetFirewallRule @paramEnableNetFirewallRule)
}
}
else
{
if ($pscmdlet.ShouldProcess('Firewall Rules for Remote Desktop', 'Enable'))
{
# Alternative Approach: Enable the minimum, not the Group
Get-NetFirewallRule -Name 'RemoteDesktop-UserMode-In-TCP' -ErrorAction SilentlyContinue | Where-Object {
$_.Enabled -ne $true
} | Enable-NetFirewallRule @paramEnableNetFirewallRule
Get-NetFirewallRule -DisplayName 'Remote Desktop - User Mode (TCP-In)' -ErrorAction SilentlyContinue | Where-Object {
$_.Enabled -ne $true
} | Enable-NetFirewallRule @paramEnableNetFirewallRule
}
}
if ($pscmdlet.ShouldProcess('Ping', 'Enable'))
{
# Allow Ping for IPv4 and IPv6
# NOTE: The wildcard (ICMPv?) will select both. Replace it with 4 or 6 to use just one of them
Get-NetFirewallRule -DisplayName 'File and Printer Sharing (Echo Request - ICMPv?-In)' -ErrorAction SilentlyContinue | Where-Object {
$_.Enabled -ne $true
} | Enable-NetFirewallRule @paramEnableNetFirewallRule
}
}
#region LICENSE
<#
BSD 3-Clause License
Copyright (c) 2021, enabling Technology
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#>
#endregion LICENSE
#region DISCLAIMER
<#
DISCLAIMER:
- Use at your own risk, etc.
- This is open-source software, if you find an issue try to fix it yourself. There is no support and/or warranty in any kind
- This is a third-party Software
- The developer of this Software is NOT sponsored by or affiliated with Microsoft Corp (MSFT) or any of its subsidiaries in any way
- The Software is not supported by Microsoft Corp (MSFT)
- By using the Software, you agree to the License, Terms, and any Conditions declared and described above
- If you disagree with any of the terms, and any conditions declared: Just delete it and build your own solution
#>
#endregion DISCLAIMER

Some files were not shown because too many files have changed in this diff Show More