ACL script to determine folder permissions

To determine the ACL’s on a specific foldertree use the following script. It will display the Path, FileSystemRights, IsInherited, Name of the underlying folders.

$path = "\\server\path\"
$targetFile = "file.csv" # Not working yet

$foldersToQuery = Get-ChildItem $Path | Where {$_.PSIsContainer} | select -expandproperty FullName

   # Get access list, change any domain
foreach ($folder in $foldersToQuery) {
    $Access = (Get-Acl $Folder).Access |
      Select-Object @{n='Path';e={ $Folder }}, *,
        @{n='ADObject';e={ 
          If ($_.IdentityReference -NotMatch "^(NT AUTH|BUILTIN|$($Env:ComputerName))") {
            $Searcher = [ADSISearcher]"(sAMAccountName=$($_.IdentityReference -Replace '^.+\\'))"
            $Searcher.PropertiesToLoad.AddRange(@("name", "distinguishedName", "objectClass"))
            $Searcher.FindOne()
        } }} |
      Select-Object *, @{n='Name';e={ $_.ADObject.Properties['name'][0] }},
        @{n='DN';e={ $_.ADObject.Properties['distinguishedname'][0] }},
        @{n='Class';e={ ([String[]]$_.ADObject.Properties['objectclass'])[-1] }} -Exclude ADObject

      $Access | ForEach-Object {
        $Entry = $_
        If ($Entry.Class -eq 'group') {
  
          $Searcher = [ADSISearcher]"(memberOf:1.2.840.113556.1.4.1941:=$($Entry.DN))"
          $Searcher.PageSize = 1000
          $Searcher.PropertiesToLoad.AddRange(@("name", "distinguishedName", "objectClass"))
          $Searcher.FindAll() | ForEach-Object {
            $ADObject = $_
            $Entry | Select-Object *, @{n='Name';e={ $ADObject.Properties['name'][0] }},
              @{n='DN';e={ $ADObject.Properties['distinguishedname'][0] }},
              @{n='Class';e={ ([String[]]$ADObject.Properties['objectclass'])[-1] }} -Exclude Name, DN, Class
          }
        } Else {
          $Entry
        }
      } | ft Path, FileSystemRights, IsInherited, Name, class -AutoSize  

}

Microsoft Teams – Welcome Message

Microsoft teams can send welcome messages when a user is added to the teams. This behavior can be altered with powershell

The current setting for the team can be checked with powershell. To run this, you need to be connected to Microsoft 365. For Microsoft 365 management you can install the “Microsoft Exchange Online Powershell Module” from Microsoft 365.

Get-UnifiedGroup -Identity 'Group Name' | fl WelcomeMessageEnabled

The outcome should be True or False. When it is set to True, welcome messages will be send. To change this setting, so welcome messages are not send, change the value to false

set-UnifiedGroup -Identity 'Group Name' -UnifiedGroupWelcomeMessageEnabled:$False

Delete folders with ‘Long Filenames’

When deleting files the following error occurs:

Source Path Too Long
The source file name(s) are larger than is supported by the file system. Try moving to a location which has a shorter path name, or try renaming to shorter name(s) before attempting this operation.

To delete these files use the following robocopy method:

MD C:\DELETE-ME
robocopy C:\DELETE-ME C:\{path}\{The Top Level Folder To Delete} /s /mir

After the directory has been removed, remove the remaining folders with:

rd C:\DELETE-ME
rd C:\{path}\{The Top Level Folder To Delete}

Backup all SQL databases

The following script can be used to backup all databases from a SQL server. The system databases are excluded. If any other database should be excluded, add them to the “NOT IN” section.

For different date formats in the export, check: https://robbamforth.wordpress.com/2010/06/11/sql-date-formats-getdate-examples/

The script is exporting the databases now with the following format: databasename_YYMMDD.bak

DECLARE @name VARCHAR(50) -- database name  
DECLARE @path VARCHAR(256) -- path for backup files  
DECLARE @fileName VARCHAR(256) -- filename for backup  
DECLARE @fileDate VARCHAR(20) -- used for file name
 
-- specify database backup directory
SET @path = 'C:\Backup\'  
 
-- specify filename format
SELECT @fileDate = CONVERT(VARCHAR(8),GETDATE(),112) 
 
DECLARE db_cursor CURSOR READ_ONLY FOR  
SELECT name 
FROM master.sys.databases 
WHERE name NOT IN ('master','model','msdb','tempdb')  -- exclude these databases
AND state = 0 -- database is online
AND is_in_standby = 0 -- database is not read only for log shipping
 
OPEN db_cursor   
FETCH NEXT FROM db_cursor INTO @name   
 
WHILE @@FETCH_STATUS = 0   
BEGIN   
   SET @fileName = @path + @name + '_' + @fileDate + '.BAK'  
   BACKUP DATABASE @name TO DISK = @fileName  
 
   FETCH NEXT FROM db_cursor INTO @name   
END   
 
CLOSE db_cursor   
DEALLOCATE db_cursor

Administer WID instance with SQL Server Management Studio

Windows Server Update Services (WSUS) can use a Windows Internal Database (WID). This database is manged through WSUS, but occasionally you want to administer this with SQL Server Management Studio.

Install SQL Server Management Studion (Express) on the server where WSUS has been installed. Then connect to the WID database by using the following server name:

2008R2:
\\.\pipe\MSSQL$MICROSOFT##SSEE\sql\query
2012R2: 
\\.\pipe\MICROSOFT##WID\tsql\query 

Now you can administer the instance.

If you prefer to administer the database from CLI, you can install the SQL Server Command Line Query Utility – SQLCMD.

To connec to to the database instance, run the sqlcmd utility by passing the instance name. If the database is set in single user mode you can change this by running the following commands:

Windows Server 2012R2:
Open Command Prompt with Administrator Rights
sqlcmd -S np:\\.\pipe\MICROSOFT##WID\tsql\query
alter database susdb
set multi_user
go 

or

Windows Server 2008R2:
sqlcmd -S np:\\.\pipe\MSSQL$MICROSOFT##SSEE\sql\query
alter database susdb
set multi_user
go 

Please Note. It is possible to administer WID instances this way, but it is not recommended. Changes to the WID instance can break the product or changes can be overwritten by updates of the products.

Reset 120 Day Grace Period for Windows RDS Server with PowerShell

Use the following Powershell script to reset the Grace Period of a Windows RDS Server

Because the original script has been removed from the script center ( https://gallery.technet.microsoft.com/scriptcenter/Reset-Terminal-Server-RDS-44922d91), a copy can be found below:

## This Script is intended to be used for Querying remaining time and resetting Terminal Server (RDS) Grace Licensing Period to Default 120 Days. 
## Developed by Prakash Kumar ([email protected]) May 28th 2016 
## www.adminthing.blogspot.com 
## Disclaimer: Please test this script in your test environment before executing on any production server. 
## Author will not be responsible for any misuse/damage caused by using it. 
 
Clear-Host 
$ErrorActionPreference = "SilentlyContinue" 
 
## Display current Status of remaining days from Grace period. 
$GracePeriod = (Invoke-WmiMethod -PATH (gwmi -namespace root\cimv2\terminalservices -class win32_terminalservicesetting).__PATH -name GetGracePeriodDays).daysleft 
Write-Host -fore Green ====================================================== 
Write-Host -fore Green 'Terminal Server (RDS) grace period Days remaining are' : $GracePeriod 
Write-Host -fore Green ======================================================   
Write-Host 
$Response = Read-Host "Do you want to reset Terminal Server (RDS) Grace period to Default 120 Days ? (Y/N)" 
 
if ($Response -eq "Y") { 
## Reset Terminal Services Grace period to 120 Days 
 
$definition = @" 
using System; 
using System.Runtime.InteropServices;  
namespace Win32Api 
{ 
    public class NtDll 
    { 
        [DllImport("ntdll.dll", EntryPoint="RtlAdjustPrivilege")] 
        public static extern int RtlAdjustPrivilege(ulong Privilege, bool Enable, bool CurrentThread, ref bool Enabled); 
    } 
} 
"@  
 
Add-Type -TypeDefinition $definition -PassThru 
 
$bEnabled = $false 
 
## Enable SeTakeOwnershipPrivilege 
$res = [Win32Api.NtDll]::RtlAdjustPrivilege(9, $true, $false, [ref]$bEnabled) 
 
## Take Ownership on the Key 
$key = [Microsoft.Win32.Registry]::LocalMachine.OpenSubKey("SYSTEM\CurrentControlSet\Control\Terminal Server\RCM\GracePeriod", [Microsoft.Win32.RegistryKeyPermissionCheck]::ReadWriteSubTree,[System.Security.AccessControl.RegistryRights]::takeownership) 
$acl = $key.GetAccessControl() 
$acl.SetOwner([System.Security.Principal.NTAccount]"Administrators") 
$key.SetAccessControl($acl) 
 
## Assign Full Controll permissions to Administrators on the key. 
$rule = New-Object System.Security.AccessControl.RegistryAccessRule ("Administrators","FullControl","Allow") 
$acl.SetAccessRule($rule) 
$key.SetAccessControl($acl) 
 
## Finally Delete the key which resets the Grace Period counter to 120 Days. 
Remove-Item 'HKLM:\SYSTEM\CurrentControlSet\Control\Terminal Server\RCM\GracePeriod' 
 
write-host 
Write-host -ForegroundColor Red 'Resetting, Please Wait....' 
Start-Sleep -Seconds 10  
 
  } 
 
Else  
    { 
Write-Host 
Write-Host -ForegroundColor Yellow '**You Chose not to reset Grace period of Terminal Server (RDS) Licensing' 
  } 
 
## Display Remaining Days again as final status 
tlsbln.exe 
$GracePost = (Invoke-WmiMethod -PATH (gwmi -namespace root\cimv2\terminalservices -class win32_terminalservicesetting).__PATH -name GetGracePeriodDays).daysleft 
Write-Host 
Write-Host -fore Yellow ===================================================== 
Write-Host -fore Yellow 'Terminal Server (RDS) grace period Days remaining are' : $GracePost 
Write-Host -fore Yellow ===================================================== 
 
## Cleanup of Variables 
Remove-Variable * -ErrorAction SilentlyContinue

MsSQL – Backup all SQL Server Databases

source: https://www.mssqltips.com/sqlservertip/1070/simple-script-to-backup-all-sql-server-databases/

Change @Path for the location of the backups.

DECLARE @name VARCHAR(50) — database name 
DECLARE @path VARCHAR(256) — path for backup files 
DECLARE @fileName VARCHAR(256) — filename for backup 
DECLARE @fileDate VARCHAR(20) — used for file name
 
— specify database backup directory
SET @path = ‘C:\Backup\’ 
 
— specify filename format
SELECT @fileDate = CONVERT(VARCHAR(20),GETDATE(),112)
 
— specify filename format Including Time
— SELECT @fileDate = CONVERT(VARCHAR(20),GETDATE(),112) + ‘_’ + REPLACE(CONVERT(VARCHAR(20),GETDATE(),108),’:’,”)
 
DECLARE db_cursor CURSOR READ_ONLY FOR 
SELECT name
FROM master.sys.databases
WHERE name NOT IN (‘master’,’model’,’msdb’,’tempdb’)  — exclude these databases
AND state = 0 — database is online
AND is_in_standby = 0 — database is not read only for log shipping
 
OPEN db_cursor  
FETCH NEXT FROM db_cursor INTO @name  
 
WHILE @@FETCH_STATUS = 0  
BEGIN  
   SET @fileName = @path + @name + ‘_’ + @fileDate + ‘.BAK’ 
   BACKUP DATABASE @name TO DISK = @fileName 
 WITH STATS, COMPRESSION, COPY_ONLY
   FETCH NEXT FROM db_cursor INTO @name  
END  
 
 
CLOSE db_cursor  
DEALLOCATE db_cursor

MsSQL Backup a Database

Use the following query to backup a MsSQL database. Fill in the correct database name (@name) and backup location (@path).

This script uses the date for the filename, so if multiple backups are needed on the same day make sure to add some remarks to it (@filename).

Also a COPY_ONLY object is used, so this will not interfere with the normal backup job.



— Declare variables
DECLARE @fileName VARCHAR(256) — filename for backup  
DECLARE @path VARCHAR(256) — path for backup files  
DECLARE @name VARCHAR(50) — database name  
DECLARE @fileDate VARCHAR(20) — used for file name
— specify database name
SET @name = ‘databasename’
— specify database backup directory
SET @path = ‘backup location’
— specify filename format
SELECT @fileDate = CONVERT(VARCHAR(20),GETDATE(),112)
SET @fileName = @path + @fileDate + ‘-‘ + @name + ‘.BAK’
— start backup of the database
BACKUP DATABASE @name TO DISK = @fileName WITH STATS, COMPRESSION, COPY_ONLY

Last Logon Date AD Users

source: https://gallery.technet.microsoft.com/scriptcenter/Get-Last-Logon-for-Users-c8e3eab2

The “lastLogon” Active Directory attribute is a property that is not replicated throughout the Domain Controllers.  This attribute is stored on the domain controller that received the authentication request and updated its property accordingly.  Because of this behavior, you have have experienced issues where Domain Controllers in other sites have old or empty lastLogon information.  This makes it difficult to be sure an account is truly inactive without verifying on each Domain Controller.  This can be a problem for environments that have tens or hundreds of Domain Controllers across its enterprise.

I was faced with a problem of writing a script, and like many, developed a seemingly simple solution.  The problem was the runtime was very long for the amount of Domain Controllers / User accounts for the environment.  I sought out to find the solution to this exceedingly long runtime, and I finally did.

This PowerShell will query each of your Domain Controllers only once and produce the most recent logon date/time.

To protect the informaiton about my directory, lets say my environment is between 5 and 10 Domain Controllers, with between 15 – 25k user accounts.  My runtime is between 4-5 minutes for the entire directory!  Which is a far cry faster than the hours other methods produce.

By default, this script will return the entire directory ($Username=*) and save to a CSV ($FileName).  You may call the function with a -Username to return a single account, and this will output on screen instead of a file.

<########################################################################### 
    The purpose of this PowerShell script is to collect the last logon  
    for user accounts on each DC in the domain, evaluate, and return the 
    most recent logon value. 
 
        Author:   Jeremy Reeves 
        Modified: 02/14/2018 
        Notes:    Must have RSAT Tools if running on a workstation 
 
Note: Added enabled true/false to output
###########################################################################> 
 
 
Import-Module ActiveDirectory 
 
function Get-ADUsersLastLogon($Username="*") { 
 
    $FilePath_Prefix = "C:\temp\UserLastLogon-" 
 
    function Msg ($Txt="") { 
        Write-Host "$([DateTime]::Now)    $Txt" 
    } 
 
    #Cycle each DC and gather user account lastlogon attributes 
     
    $List = @() #Define Array 
    (Get-ADDomain).ReplicaDirectoryServers | Sort | % { 
 
        $DC = $_ 
        Msg "Reading $DC" 
        $List += Get-ADUser -Server $_ -Filter "samaccountname -like '$Username'" -Properties LastLogon | Select samaccountname,lastlogon,enabled,@{n='DC';e={$DC}} 
 
    } 
 
    Msg "Sorting for most recent lastlogon" 
     
    $LatestLogOn = @() #Define Array 
    $List | Group-Object -Property samaccountname | % { 
 
        $LatestLogOn += ($_.Group | Sort -prop lastlogon -Descending)[0] 
 
    } 
     
    $List.Clear() 
 
    if ($Username -eq "*") { #$Username variable was not set.    Running against all user accounts and exporting to a file. 
 
        $FileName = "$FilePath_Prefix$([DateTime]::Now.ToString("yyyyMMdd-HHmmss")).csv" 
         
        try { 
 
            $LatestLogOn | Select samaccountname, lastlogon, @{n='lastlogondatetime';e={[datetime]::FromFileTime($_.lastlogon)}}, Enabled, DC | Export-CSV -Path $FileName -NoTypeInformation -Force 
            Msg "Exported results. $FileName" 
 
        } catch { 
 
            Msg "Export Failed. $FileName" 
 
        } 
 
    } else { #$Username variable was set, and may refer to a single user account. 
 
        if ($LatestLogOn) { $LatestLogOn | Select samaccountname, @{n='lastlogon';e={[datetime]::FromFileTime($_.lastlogon)}}, Enabled, DC | FT } else { Msg "$Username not found." } 
 
    } 
 
    $LatestLogon.Clear() 
 
}