├── .github └── FUNDING.yml ├── Monitoring ├── DNS-Change-Monitoring.ps1 ├── Dell-DCU3.1-Monitoring.ps1 ├── LolBasPrevention-Monitoring.ps1 ├── LolBasPrevention-Prerun.ps1 ├── O365-AllTenants-Account-Age-Monitoring.ps1 ├── O365-AllTenants-LargeMailboxes-Monitoring.ps1 ├── O365-AllTenants-MFA-Monitoring.ps1 ├── O365-AllTenants-NewTeams-Monitoring.ps1 ├── O365-AllTenants-SharepointSize-Monitoring.ps1 ├── O365-AuditLogRules-Monitoring.ps1 ├── O365-BlockedUser-Monitoring.ps1 ├── O365-BreakGlass-Monitoring.ps1 ├── O365-DeletedUsers-Monitoring.ps1 ├── O365-JIT-Adminsitrator-Monitoring.ps1 ├── O365-Location-Monitoring.ps1 ├── O365-NearPermanentDeletedUsers-Monitoring.ps1 ├── O365-NoSecureAppModel-MFA-Monitoring.ps1 ├── O365-SingleTenant-SharepointSize-Monitoring.ps1 ├── O365-SingleTenants-Account-Age-Monitoring.ps1 ├── O365-SingleTenants-LargeMailboxes-Monitoring.ps1 ├── O365-UnusedLicense-Monitoring.ps1 ├── O365-singleTenant-MFA-Monitoring.ps1 ├── SQL-ServerHealth-Monitoring.ps1 ├── Server-Cert-Bindings-Monitor.ps1 ├── Server-ComputerAge-Monitoring.ps1 ├── Server-DFSR-Monitoring.ps1 ├── Server-RogueDHCP-Monitoring.ps1 ├── Server-Userage-Monitoring.ps1 ├── Server-VSSSnapshotSize.ps1 ├── Shodan-Monitoring.ps1 ├── Unifi-Settings-Monitoring.ps1 ├── W10-ActiveSMB-Monitoring.ps1 ├── W10-ClientBasedVPN-Monitoring.ps1 ├── W10-GPODEployment-Monitoring.ps1 ├── W10-OneDriveKFM-Monitoring.ps1 ├── W10-OneDriveSyncStatus-Monitoring.ps1 ├── W10-PSExec-Monitoring.ps1 ├── W10-SMART-Monitoring.ps1 ├── W10-Speedtest-Monitoring.ps1 ├── W10-WOL-Monitoring.ps1 ├── W10-WinSat-Monitoring.ps1 └── WANIP-Monitoring.ps1 ├── README.md └── Remediation ├── Clear-UserRecycleBin-remediation.ps1 ├── Create-NamedAccount-withSeed.ps1 ├── Create-NamedAccount.ps1 ├── Dell-DCU-Remediation.ps1 ├── Isolate.ps1 ├── W10-Remediate-ClientBasedVPN.ps1 ├── W10-WOL-Remediation.ps1 └── remove-namedaccount.ps1 /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: [kelvintegelaar] 4 | -------------------------------------------------------------------------------- /Monitoring/DNS-Change-Monitoring.ps1: -------------------------------------------------------------------------------- 1 | $DomainsToTest = @("remote.clientname.com", "clientswebsite.com") 2 | New-item "C:\ProgramData\DNSTestLog" -ItemType Directory -erroraction SilentlyContinue -Force | out-null 3 | 4 | $DNSHealth = foreach ($DomainToTest in $DomainsToTest) { 5 | 6 | Clear-DnsClientCache 7 | 8 | $PreviousDNS = get-content "C:\ProgramData\DNSTestLog\$($DomainToTest).txt" -ErrorAction SilentlyContinue 9 | if (!$PreviousDNS) { 10 | write-host "No previous file found. Creating file. Compare will fail." 11 | "" | Out-File "C:\ProgramData\DNSTestLog\$($DomainToTest).txt" 12 | } 13 | $DNSResults = (Resolve-dnsname -name $DomainToTest -Type A -NoHostsFile).IP4Address 14 | $DNSResults | Out-File "C:\ProgramData\DNSTestLog\$($DomainToTest).txt" 15 | if ($PreviousDNS -ne $DNSResults) { 16 | "$DomainToTest does not equal the previous result." 17 | } 18 | 19 | } 20 | 21 | if (!$DNSHealth) { 22 | $DNSHealth = "Healthy" 23 | } -------------------------------------------------------------------------------- /Monitoring/Dell-DCU3.1-Monitoring.ps1: -------------------------------------------------------------------------------- 1 | $DownloadURL = "https://dl.dell.com/FOLDER05944445M/1/Dell-Command-Update_V104D_WIN_3.1.0_A00.EXE" 2 | $DownloadLocation = "C:\Temp" 3 | 4 | try { 5 | $TestDownloadLocation = Test-Path $DownloadLocation 6 | if (!$TestDownloadLocation) { new-item $DownloadLocation -ItemType Directory -force } 7 | $TestDownloadLocationZip = Test-Path "$DownloadLocation\DellCommandUpdate.exe" 8 | if (!$TestDownloadLocationZip) { 9 | Invoke-WebRequest -UseBasicParsing -Uri $DownloadURL -OutFile "$($DownloadLocation)\DellCommandUpdate.exe" 10 | Start-Process -FilePath "$($DownloadLocation)\DellCommandUpdate.exe" -ArgumentList '/s' -Verbose -Wait 11 | set-service -name 'DellClientManagementService' -StartupType Manual 12 | } 13 | 14 | } 15 | catch { 16 | write-host "The download and installation of DCUCli failed. Error: $($_.Exception.Message)" 17 | exit 1 18 | } 19 | 20 | start-process "C:\Program Files\Dell\CommandUpdate\dcu-cli.exe" -ArgumentList "/scan -report=$DownloadLocation" -Wait 21 | [ xml]$XMLReport = get-content "$DownloadLocation\DCUApplicableUpdates.xml" 22 | #We now remove the item, because we don't need it anymore, and sometimes fails to overwrite 23 | remove-item "$DownloadLocation\DCUApplicableUpdates.xml" -Force 24 | 25 | $AvailableUpdates = $XMLReport.updates.update 26 | 27 | $BIOSUpdates = ($XMLReport.updates.update | Where-Object { $_.type -eq "BIOS" }).name.Count 28 | $ApplicationUpdates = ($XMLReport.updates.update | Where-Object { $_.type -eq "Application" }).name.Count 29 | $DriverUpdates = ($XMLReport.updates.update | Where-Object { $_.type -eq "Driver" }).name.Count 30 | $FirmwareUpdates = ($XMLReport.updates.update | Where-Object { $_.type -eq "Firmware" }).name.Count 31 | $OtherUpdates = ($XMLReport.updates.update | Where-Object { $_.type -eq "Other" }).name.Count 32 | $PatchUpdates = ($XMLReport.updates.update | Where-Object { $_.type -eq "Patch" }).name.Count 33 | $UtilityUpdates = ($XMLReport.updates.update | Where-Object { $_.type -eq "Utility" }).name.Count 34 | $UrgentUpdates = ($XMLReport.updates.update | Where-Object { $_.Urgency -eq "Urgent" }).name.Count -------------------------------------------------------------------------------- /Monitoring/LolBasPrevention-Monitoring.ps1: -------------------------------------------------------------------------------- 1 | $ScriptBlockLogging = get-ItemProperty -Path "HKLM:\SOFTWARE\Wow6432Node\Policies\Microsoft\Windows\PowerShell\ScriptBlockLogging" 2 | $ScriptBlockEnable = if ($ScriptBlockLogging.EnableScriptBLockLogging -ne 1) { "Error - Script Block Logging is not enabled" } else { "Healthy - Script Block Logging is enabled" } 3 | 4 | $DangerousCommands = @("iwr", "irm", "curl", "saps", "iex", "Invoke-Expression", "Invoke-RestMethod", "Invoke-WebRequest", "Invoke-RestMethod") 5 | $Aliasses = get-alias | Where-Object { $_.name -in $DangerousCommands -and $_.ResolvedCommandName -ne "Write-Host" } 6 | if (!$Aliasses) { 7 | $AliasProtection = "Healthy - Dangerous commands are protected." 8 | } 9 | else { 10 | $AliasProtection = "Unhealthy - Dangerous commands are not protected. Please investigate." 11 | } 12 | $logInfo = @{ 13 | ProviderName = "Microsoft-Windows-PowerShell" 14 | StartTime = (get-date).AddHours(-2) 15 | } 16 | $PowerShellEvents = Get-WinEvent -FilterHashtable $logInfo | Select-Object TimeCreated, message 17 | $PowerShellLogs = foreach ($Event in $PowerShellEvents) { 18 | 19 | foreach ($command in $DangerousCommands) { 20 | if ($Event.Message -like "*$Command*") { 21 | [pscustomobject] @{ 22 | TimeCreated = $event.TimeCreated 23 | EventMessage = $Event.message 24 | TriggeredCommand = $command 25 | 26 | } 27 | } 28 | } 29 | 30 | } 31 | 32 | if(!$PowerShellLogs){ 33 | $PowerShellLogs = "Healthy" 34 | } -------------------------------------------------------------------------------- /Monitoring/LolBasPrevention-Prerun.ps1: -------------------------------------------------------------------------------- 1 | New-Item -Path "HKLM:\SOFTWARE\Wow6432Node\Policies\Microsoft\Windows\PowerShell\ScriptBlockLogging" -Force 2 | Set-ItemProperty -Path "HKLM:\SOFTWARE\Wow6432Node\Policies\Microsoft\Windows\PowerShell\ScriptBlockLogging" -Name "EnableScriptBlockLogging" -Value 1 -Force 3 | #Set aliasses to something harmless 4 | $DangerousCommands = @("iwr", "irm", "curl", "saps", "iex", "Invoke-Expression", "Invoke-RestMethod", "Invoke-WebRequest", "Invoke-RestMethod") 5 | foreach ($Command in $DangerousCommands) { 6 | Set-Alias -Name $Command -Value "write-host" -Option AllScope -Force 7 | } -------------------------------------------------------------------------------- /Monitoring/O365-AllTenants-Account-Age-Monitoring.ps1: -------------------------------------------------------------------------------- 1 | ########################## Secure App Model Settings ############################ 2 | $ApplicationId = 'YourApplicationID' 3 | $ApplicationSecret = 'YourApplicationSecret' | Convertto-SecureString -AsPlainText -Force 4 | $TenantID = 'YourTenantID' 5 | $RefreshToken = 'YourRefreshToken' 6 | $UPN = "YourUPN" 7 | ########################## Script Settings ############################ 8 | $Date = (get-date).AddDays((-90)) 9 | $Baseuri = "https://graph.microsoft.com/beta" 10 | write-host "Generating token to log into Azure AD. Grabbing all tenants" -ForegroundColor Green 11 | $credential = New-Object System.Management.Automation.PSCredential($ApplicationId, $ApplicationSecret) 12 | $aadGraphToken = New-PartnerAccessToken -ApplicationId $ApplicationId -Credential $credential -RefreshToken $refreshToken -Scopes 'https://graph.windows.net/.default' -ServicePrincipal -Tenant $tenantID 13 | $graphToken = New-PartnerAccessToken -ApplicationId $ApplicationId -Credential $credential -RefreshToken $refreshToken -Scopes 'https://graph.microsoft.com/.default' -ServicePrincipal -Tenant $tenantID 14 | Connect-AzureAD -AadAccessToken $aadGraphToken.AccessToken -AccountId $upn -MsAccessToken $graphToken.AccessToken -TenantId $tenantID | Out-Null 15 | $tenants = Get-AzureAdContract -All:$true 16 | Disconnect-AzureAD 17 | $OldObjects = foreach ($Tenant in $Tenants) { 18 | 19 | $CustGraphToken = New-PartnerAccessToken -ApplicationId $ApplicationId -Credential $credential -RefreshToken $refreshToken -Scopes "https://graph.microsoft.com/.default" -ServicePrincipal -Tenant $tenant.CustomerContextId 20 | write-host "$($Tenant.Displayname): Starting process." -ForegroundColor Green 21 | $Header = @{ 22 | Authorization = "Bearer $($CustGraphToken.AccessToken)" 23 | } 24 | write-host " $($Tenant.Displayname): Grabbing all Users that have not logged in for 90 days." -ForegroundColor Green 25 | $UserList = (Invoke-RestMethod -Uri "$baseuri/users/?`$select=displayName,UserPrincipalName,signInActivity" -Headers $Header -Method get -ContentType "application/json").value | select-object DisplayName,UserPrincipalName,@{Name='LastLogon';Expression={[datetime]::Parse($_.SignInActivity.lastSignInDateTime)}} | Where-Object { $_.LastLogon -lt $Date } 26 | $devicesList = (Invoke-RestMethod -Uri "$baseuri/devices" -Headers $Header -Method get -ContentType "application/json").value | select-object Displayname,@{Name='LastLogon';Expression={[datetime]::Parse($_.approximateLastSignInDateTime)}} 27 | 28 | 29 | [PSCustomObject]@{ 30 | Users = $UserList | where-object {$_.LastLogon -ne $null} 31 | Devices = $devicesList | Where-Object {$_.LastLogon -lt $Date} 32 | } 33 | } 34 | 35 | if(!$OldObjects) { write-host "No old objects found in any tenant"} else { write-host "Old objects found."; $Oldobjects} -------------------------------------------------------------------------------- /Monitoring/O365-AllTenants-LargeMailboxes-Monitoring.ps1: -------------------------------------------------------------------------------- 1 | $ApplicationId = 'xxxx-xxxx-xxx-xxxx-xxxx' 2 | $ApplicationSecret = 'TheSecretTheSecret' | Convertto-SecureString -AsPlainText -Force 3 | $TenantID = 'YourTenantID' 4 | $RefreshToken = 'RefreshToken' 5 | $ExchangeRefreshToken = 'ExchangeRefreshToken' 6 | $upn = 'UPN-Used-To-Generate-Tokens' 7 | $SizeToMonitor = 60 8 | 9 | $credential = New-Object System.Management.Automation.PSCredential($ApplicationId, $ApplicationSecret) 10 | $aadGraphToken = New-PartnerAccessToken -ApplicationId $ApplicationId -Credential $credential -RefreshToken $refreshToken -Scopes 'https://graph.windows.net/.default' -ServicePrincipal -Tenant $tenantID 11 | $graphToken = New-PartnerAccessToken -ApplicationId $ApplicationId -Credential $credential -RefreshToken $refreshToken -Scopes 'https://graph.microsoft.com/.default' -ServicePrincipal -Tenant $tenantID 12 | Connect-MsolService -AdGraphAccessToken $aadGraphToken.AccessToken -MsGraphAccessToken $graphToken.AccessToken 13 | $customers = Get-MsolPartnerContract -All 14 | $LargeMailboxes = @() 15 | foreach ($customer in $customers) { 16 | write-host "Getting started for $($Customer.name)" -foregroundcolor green 17 | $token = New-PartnerAccessToken -ApplicationId 'a0c73c16-a7e3-4564-9a95-2bdf47383716'-RefreshToken $ExchangeRefreshToken -Scopes 'https://outlook.office365.com/.default' -Tenant $customer.TenantId 18 | $tokenValue = ConvertTo-SecureString "Bearer $($token.AccessToken)" -AsPlainText -Force 19 | $credential = New-Object System.Management.Automation.PSCredential($upn, $tokenValue) 20 | $customerId = $customer.DefaultDomainName 21 | $session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri "https://ps.outlook.com/powershell-liveid?DelegatedOrg=$($customerId)&BasicAuthToOAuthConversion=true" -Credential $credential -Authentication Basic -AllowRedirection 22 | Import-PSSession $session -allowclobber -Disablenamechecking 23 | $Mailboxes = Get-Mailbox | Get-MailboxStatistics | Select-Object DisplayName, @{name = "TotalItemSize (GB)"; expression = { [math]::Round((($_.TotalItemSize.Value.ToString()).Split("(")[1].Split(" ")[0].Replace(",", "") / 1GB), 2) } }, ItemCount | Sort "TotalItemSize (GB)" -Descending 24 | foreach ($Mailbox in $Mailboxes) { if ($Mailbox.'TotalItemSize (GB)' -gt $SizeToMonitor) { $LargeMailboxes += $Mailbox } } 25 | Remove-PSSession $session 26 | } 27 | 28 | if (!$LargeMailboxes) { "No Large mailboxes found" } -------------------------------------------------------------------------------- /Monitoring/O365-AllTenants-MFA-Monitoring.ps1: -------------------------------------------------------------------------------- 1 | $ApplicationId = 'xxxx-xxxx-xxxx-xxxx-xxx' 2 | $ApplicationSecret = 'YOURSECRET' | Convertto-SecureString -AsPlainText -Force 3 | $TenantID = 'xxxxxx-xxxx-xxx-xxxx--xxx' 4 | $RefreshToken = 'LongResourcetoken' 5 | $ExchangeRefreshToken = 'LongExchangeToken' 6 | $credential = New-Object System.Management.Automation.PSCredential($ApplicationId, $ApplicationSecret) 7 | $aadGraphToken = New-PartnerAccessToken -ApplicationId $ApplicationId -Credential $credential -RefreshToken $refreshToken -Scopes 'https://graph.windows.net/.default' -ServicePrincipal -Tenant $tenantID 8 | $graphToken = New-PartnerAccessToken -ApplicationId $ApplicationId -Credential $credential -RefreshToken $refreshToken -Scopes 'https://graph.microsoft.com/.default' -ServicePrincipal -Tenant $tenantID 9 | 10 | Connect-MsolService -AdGraphAccessToken $aadGraphToken.AccessToken -MsGraphAccessToken $graphToken.AccessToken 11 | $customers = Get-MsolPartnerContract -All 12 | $MFAType = foreach ($customer in $customers) { 13 | $users = Get-MsolUser -TenantId $customer.tenantid -all 14 | 15 | foreach ($user in $users) { 16 | $primaryMFA = if ($null -ne $user.StrongAuthenticationUserDetails) { ($user.StrongAuthenticationMethods | Where-Object { $_.IsDefault -eq $true }).methodType } else { "MFA Disabled" } 17 | $SecondaryMFA = if ($null -ne $user.StrongAuthenticationUserDetails) { ($user.StrongAuthenticationMethods | Where-Object { $_.IsDefault -eq $false }).methodType } else { "No Secondary Option enabled" } 18 | [PSCustomObject]@{ 19 | "DisplayName" = $user.DisplayName 20 | "user" = $user.UserPrincipalName 21 | "Primary MFA" = $primaryMFA 22 | "Secondary MFA" = $SecondaryMFA 23 | } 24 | } 25 | } 26 | 27 | $UnSafeMFAUsers = $MFAType | Where-Object { $_.'Primary MFA' -like "*SMS*" -or $_.'Primary MFA' -like "*voice*" -or $_.'Primary MFA' -like "*OTP*" } 28 | 29 | if (!$UnSafeMFAUsers) { 30 | $UnSafeMFAUsers = "Healthy" 31 | } -------------------------------------------------------------------------------- /Monitoring/O365-AllTenants-NewTeams-Monitoring.ps1: -------------------------------------------------------------------------------- 1 | ############################## 2 | $MonitorDate = (get-date).AddDays(-1) 3 | $ApplicationId = 'XXXX-XXXX-XXXX-XXX-XXX' 4 | $ApplicationSecret = 'YourApplicationSecret' | Convertto-SecureString -AsPlainText -Force 5 | $TenantID = 'YourTenantID.Onmicrosoft.com' 6 | $RefreshToken = 'VeryLongRefreshToken' 7 | ############################## 8 | $credential = New-Object System.Management.Automation.PSCredential($ApplicationId, $ApplicationSecret) 9 | write-host "Generating access tokens" -ForegroundColor Green 10 | $graphToken = New-PartnerAccessToken -ApplicationId $ApplicationId -Credential $credential -RefreshToken $refreshToken -Scopes 'https://graph.microsoft.com/.default' -ServicePrincipal -Tenant $tenantID 11 | write-host "Connecting to MSOLService" -ForegroundColor Green 12 | Connect-MsolService -AdGraphAccessToken $aadGraphToken.AccessToken -MsGraphAccessToken $graphToken.AccessToken 13 | write-host "Grabbing client list" -ForegroundColor Green 14 | $customers = Get-MsolPartnerContract -All 15 | write-host "Connecting to clients" -ForegroundColor Green 16 | 17 | foreach ($customer in $customers) { 18 | write-host "Generating token for $($Customer.name)" -ForegroundColor Green 19 | $graphToken = New-PartnerAccessToken -ApplicationId $ApplicationId -Credential $credential -RefreshToken $refreshToken -Scopes 'https://graph.microsoft.com/.default' -ServicePrincipal -Tenant $customer.TenantID 20 | $Header = @{ 21 | Authorization = "Bearer $($graphToken.AccessToken)" 22 | } 23 | write-host "Grabbing Teams for $($Customer.name)" -ForegroundColor Green 24 | $GroupUri = "https://graph.microsoft.com/v1.0/Groups?`$top=999" 25 | $Groups = (Invoke-RestMethod -Uri $GroupUri -Headers $Header -Method Get -ContentType "application/json").value | Where-Object { $_.resourceProvisioningOptions -eq "Team" } 26 | $NewGroups = foreach ($group in $Groups | Where-Object { [datetime]$_.CreatedDateTime -gt $MonitorDate }) { 27 | "$($Group.displayName) has been created on $($group.createdDateTime)" 28 | 29 | } 30 | } 31 | if(!$NewGroups){ $NewGroups = "Healthy. No New groups have been created."} -------------------------------------------------------------------------------- /Monitoring/O365-AllTenants-SharepointSize-Monitoring.ps1: -------------------------------------------------------------------------------- 1 | $ApplicationId = 'YOURAPPLICATIONID' 2 | $ApplicationSecret = 'YOURAPPLICATIONSECRET' | Convertto-SecureString -AsPlainText -Force 3 | $TenantID = 'YOURTENANTID' 4 | $RefreshToken = 'YOURUNBELIEVEBALLYLONGREFRESHTOKEN' 5 | $upn = 'UPN-Used-To-Generate-Tokens' 6 | ############################## 7 | $credential = New-Object System.Management.Automation.PSCredential($ApplicationId, $ApplicationSecret) 8 | write-host "Generating access tokens" -ForegroundColor Green 9 | $aadGraphToken = New-PartnerAccessToken -ApplicationId $ApplicationId -Credential $credential -RefreshToken $refreshToken -Scopes 'https://graph.windows.net/.default' -ServicePrincipal -Tenant $tenantID 10 | $graphToken = New-PartnerAccessToken -ApplicationId $ApplicationId -Credential $credential -RefreshToken $refreshToken -Scopes 'https://graph.microsoft.com/.default' -ServicePrincipal -Tenant $tenantID 11 | write-host "Connecting to MSOLService" -ForegroundColor Green 12 | Connect-MsolService -AdGraphAccessToken $aadGraphToken.AccessToken -MsGraphAccessToken $graphToken.AccessToken 13 | write-host "Grabbing client list" -ForegroundColor Green 14 | $customers = Get-MsolPartnerContract -All 15 | write-host "Connecting to clients" -ForegroundColor Green 16 | 17 | $LimitsReached = foreach ($customer in $customers) { 18 | write-host "Generating token for $($Customer.name)" -ForegroundColor Green 19 | $graphToken = New-PartnerAccessToken -ApplicationId $ApplicationId -Credential $credential -RefreshToken $refreshToken -Scopes 'https://graph.microsoft.com/.default' -ServicePrincipal -Tenant $customer.TenantID 20 | $Header = @{ 21 | Authorization = "Bearer $($graphToken.AccessToken)" 22 | } 23 | Write-Host "Grabbing data for $($customer.name)" -ForegroundColor green 24 | $OneDriveUsageURI = "https://graph.microsoft.com/v1.0/reports/getOneDriveUsageAccountDetail(period='D7')" 25 | $OneDriveUsageReports = (Invoke-RestMethod -Uri $OneDriveUsageURI -Headers $Header -Method Get -ContentType "application/json") | ConvertFrom-Csv 26 | 27 | $SharepointUsageReportsURI = "https://graph.microsoft.com/v1.0/reports/getSharePointSiteUsageDetail(period='D7')" 28 | $SharepointUsageReports = (Invoke-RestMethod -Uri $SharepointUsageReportsURI -Headers $Header -Method Get -ContentType "application/json") | ConvertFrom-Csv 29 | 30 | 31 | foreach ($SharepointReport in $SharepointUsageReports) { 32 | if ([int]$SharepointReport.'File count' -ge [int]"90000") { 33 | $SharepointReport 34 | } 35 | } 36 | 37 | foreach ($OneDriveReport in $OneDriveUsageReports) { 38 | if ([int]$OneDriveReport.'File count' -ge [int]"90000") { 39 | $OneDriveReport 40 | } 41 | } 42 | 43 | 44 | 45 | } 46 | 47 | if (!$LimitsReached) { 48 | Write-Host "Healthy" -ForegroundColor green 49 | } 50 | else { 51 | Write-Host "Unhealthy" -ForegroundColor Red 52 | $LimitsReached 53 | } -------------------------------------------------------------------------------- /Monitoring/O365-AuditLogRules-Monitoring.ps1: -------------------------------------------------------------------------------- 1 | $ApplicationId = 'YourApplicationID' 2 | $ApplicationSecret = 'YourApplicationSecret' | Convertto-SecureString -AsPlainText -Force 3 | $TenantID = 'YourTenantID' 4 | $ExchangeRefreshToken = 'YourExchangeToken' 5 | $RefreshToken = 'YourRefreshToken' 6 | $UPN = "UPN-Used-To-Generate-Token" 7 | ############################## 8 | $credential = New-Object System.Management.Automation.PSCredential($ApplicationId, $ApplicationSecret) 9 | 10 | $aadGraphToken = New-PartnerAccessToken -ApplicationId $ApplicationId -Credential $credential -RefreshToken $refreshToken -Scopes 'https://graph.windows.net/.default' -ServicePrincipal -Tenant $tenantID 11 | $graphToken = New-PartnerAccessToken -ApplicationId $ApplicationId -Credential $credential -RefreshToken $refreshToken -Scopes 'https://graph.microsoft.com/.default' -ServicePrincipal -Tenant $tenantID 12 | 13 | Connect-MsolService -AdGraphAccessToken $aadGraphToken.AccessToken -MsGraphAccessToken $graphToken.AccessToken 14 | $customers = Get-MsolPartnerContract -All 15 | $logs = foreach ($customer in $customers) { 16 | 17 | $startDate = (Get-Date).AddDays(-1) 18 | $endDate = (Get-Date) 19 | $token = New-PartnerAccessToken -ApplicationId 'a0c73c16-a7e3-4564-9a95-2bdf47383716'-RefreshToken $ExchangeRefreshToken -Scopes 'https://outlook.office365.com/.default' -Tenant $customer.TenantId 20 | $tokenValue = ConvertTo-SecureString "Bearer $($token.AccessToken)" -AsPlainText -Force 21 | $credential = New-Object System.Management.Automation.PSCredential($upn, $tokenValue) 22 | $customerId = $customer.DefaultDomainName 23 | $session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri "https://ps.outlook.com/powershell-liveid?DelegatedOrg=$($customerId)&BasicAuthToOAuthConversion=true" -Credential $credential -Authentication Basic -AllowRedirection 24 | $s = import-PSSession $session -AllowClobber -CommandName "Search-unifiedAuditLog", "Get-AdminAuditLogConfig" 25 | if((Get-AdminAuditLogConfig).UnifiedAuditLogIngestionEnabled -eq $false){ 26 | write-host "AuditLog is disabled for client $($customer.name)" 27 | } 28 | 29 | $LogsTenant = @() 30 | Write-Host "Retrieving logs for $($customer.name)" -ForegroundColor Blue 31 | do { 32 | $logsTenant += Search-unifiedAuditLog -SessionCommand ReturnLargeSet -SessionId $customer.name -ResultSize 5000 -StartDate $startDate -EndDate $endDate -Operations "New-InboxRule", "Set-InboxRule", "UpdateInboxRules" 33 | Write-Host "Retrieved $($logsTenant.count) logs" -ForegroundColor Yellow 34 | }while ($LogsTenant.count % 5000 -eq 0 -and $LogsTenant.count -ne 0) 35 | Write-Host "Finished Retrieving logs" -ForegroundColor Green 36 | $LogsTenant 37 | } 38 | foreach($log in $logs){ 39 | $AuditData = $log.AuditData | ConvertFrom-Json 40 | Write-Host "A new or changed rule has been found for user $($log.UserIds). The rule has the following info: $($Auditdata.Parameters | out-string)`n" 41 | } 42 | if(!$Logs){ 43 | write-host "Healthy." 44 | } -------------------------------------------------------------------------------- /Monitoring/O365-BlockedUser-Monitoring.ps1: -------------------------------------------------------------------------------- 1 | ############################## 2 | $ApplicationId = 'XXXX-XXXX-XXXX-XXX-XXX' 3 | $ApplicationSecret = 'YourApplicationSecret' | Convertto-SecureString -AsPlainText -Force 4 | $TenantID = 'YourTenantID.Onmicrosoft.com' 5 | $RefreshToken = 'VeryLongRefreshToken' 6 | ############################## 7 | $credential = New-Object System.Management.Automation.PSCredential($ApplicationId, $ApplicationSecret) 8 | $aadGraphToken = New-PartnerAccessToken -ApplicationId $ApplicationId -Credential $credential -RefreshToken $refreshToken -Scopes 'https://graph.windows.net/.default' -ServicePrincipal -Tenant $tenantID 9 | $graphToken = New-PartnerAccessToken -ApplicationId $ApplicationId -Credential $credential -RefreshToken $refreshToken -Scopes 'https://graph.microsoft.com/.default' -ServicePrincipal -Tenant $tenantID 10 | Connect-MsolService -AdGraphAccessToken $aadGraphToken.AccessToken -MsGraphAccessToken $graphToken.AccessToken 11 | $customers = Get-MsolPartnerContract -All 12 | 13 | $credential = New-Object System.Management.Automation.PSCredential($ApplicationId, $ApplicationSecret) 14 | $aadGraphToken = New-PartnerAccessToken -ApplicationId $ApplicationId -Credential $credential -RefreshToken $refreshToken -Scopes 'https://graph.windows.net/.default' -ServicePrincipal -Tenant $tenantID 15 | $graphToken = New-PartnerAccessToken -ApplicationId $ApplicationId -Credential $credential -RefreshToken $refreshToken -Scopes 'https://graph.microsoft.com/.default' -ServicePrincipal -Tenant $tenantID 16 | Connect-MsolService -AdGraphAccessToken $aadGraphToken.AccessToken -MsGraphAccessToken $graphToken.AccessToken 17 | $customers = Get-MsolPartnerContract -All 18 | $BlockedUserlist = foreach ($customer in $customers) { 19 | write-host "Getting Blocked users for $($Customer.name)" -ForegroundColor Green 20 | $BlockedUsers = Get-MsolUser -TenantId $($customer.TenantID) | Where-Object {$_.BlockCredential -eq $true} 21 | foreach($User in $BlockedUsers){ "$($user.UserPrincipalName) is blocked from logon." } 22 | } 23 | if(!$BlockedUserlist) { $BlockedUserlist = "Healthy" } -------------------------------------------------------------------------------- /Monitoring/O365-BreakGlass-Monitoring.ps1: -------------------------------------------------------------------------------- 1 | #We match on the following account. prefix all your breakglass accounts with this. e.g. "Breakglass-1234321" 2 | $BreakGlassUser = "Breakglass*" 3 | ############################################################ 4 | $ApplicationId = 'xxxx-xxxx-xxx-xxxx-xxxx' 5 | $ApplicationSecret = 'TheSecretTheSecrey' | Convertto-SecureString -AsPlainText -Force 6 | $TenantID = 'YourTenantID' 7 | $RefreshToken = 'RefreshToken' 8 | $ExchangeRefreshToken = 'ExchangeRefreshToken' 9 | $upn = 'UPN-Used-To-Generate-Tokens' 10 | ############################################################# 11 | $credential = New-Object System.Management.Automation.PSCredential($ApplicationId, $ApplicationSecret) 12 | 13 | $aadGraphToken = New-PartnerAccessToken -ApplicationId $ApplicationId -Credential $credential -RefreshToken $refreshToken -Scopes 'https://graph.windows.net/.default' -ServicePrincipal -Tenant $tenantID 14 | $graphToken = New-PartnerAccessToken -ApplicationId $ApplicationId -Credential $credential -RefreshToken $refreshToken -Scopes 'https://graph.microsoft.com/.default' -ServicePrincipal -Tenant $tenantID 15 | 16 | Connect-MsolService -AdGraphAccessToken $aadGraphToken.AccessToken -MsGraphAccessToken $graphToken.AccessToken 17 | $customers = Get-MsolPartnerContract -All 18 | foreach ($customer in $customers) { 19 | $token = New-PartnerAccessToken -ApplicationId 'a0c73c16-a7e3-4564-9a95-2bdf47383716'-RefreshToken $ExchangeRefreshToken -Scopes 'https://outlook.office365.com/.default' -Tenant $customer.TenantId 20 | $tokenValue = ConvertTo-SecureString "Bearer $($token.AccessToken)" -AsPlainText -Force 21 | $credential = New-Object System.Management.Automation.PSCredential($upn, $tokenValue) 22 | $customerId = $customer.DefaultDomainName 23 | $session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri "https://ps.outlook.com/powershell-liveid?DelegatedOrg=$($customerId)&amp;BasicAuthToOAuthConversion=true" -Credential $credential -Authentication Basic -AllowRedirection 24 | Import-PSSession $session -allowclobber -DisableNameChecking 25 | $startDate = (Get-Date).AddDays(-1) 26 | $endDate = (Get-Date) 27 | $Logs = @() 28 | Write-Host "Retrieving logs for $($customer.name)" -ForegroundColor Blue 29 | do { 30 | $logs += Search-unifiedAuditLog -SessionCommand ReturnLargeSet -SessionId $customer.name -ResultSize 5000 -StartDate $startDate -EndDate $endDate -Operations UserLoggedIn 31 | Write-Host "Retrieved $($logs.count) logs" -ForegroundColor Yellow 32 | }while ($Logs.count % 5000 -eq 0 -and $logs.count -ne 0) 33 | Write-Host "Finished Retrieving logs" -ForegroundColor Green 34 | $logs | Select-Object UserIds, Operations, CreationDate | Where-Object {$_.UserIds -like $BreakGlassUser} 35 | } -------------------------------------------------------------------------------- /Monitoring/O365-DeletedUsers-Monitoring.ps1: -------------------------------------------------------------------------------- 1 | ############################## 2 | $ApplicationId = 'XXXX-XXXX-XXXX-XXX-XXX' 3 | $ApplicationSecret = 'YourApplicationSecret' | Convertto-SecureString -AsPlainText -Force 4 | $TenantID = 'YourTenantID.Onmicrosoft.com' 5 | $RefreshToken = 'VeryLongRefreshToken' 6 | ############################## 7 | $credential = New-Object System.Management.Automation.PSCredential($ApplicationId, $ApplicationSecret) 8 | $aadGraphToken = New-PartnerAccessToken -ApplicationId $ApplicationId -Credential $credential -RefreshToken $refreshToken -Scopes 'https://graph.windows.net/.default' -ServicePrincipal -Tenant $tenantID 9 | $graphToken = New-PartnerAccessToken -ApplicationId $ApplicationId -Credential $credential -RefreshToken $refreshToken -Scopes 'https://graph.microsoft.com/.default' -ServicePrincipal -Tenant $tenantID 10 | Connect-MsolService -AdGraphAccessToken $aadGraphToken.AccessToken -MsGraphAccessToken $graphToken.AccessToken 11 | $customers = Get-MsolPartnerContract -All 12 | $DeletedUserlist = foreach ($customer in $customers) { 13 | write-host "Getting Deleted users for $($Customer.name)" -ForegroundColor Green 14 | $DeletedUsers = Get-MsolUser -ReturnDeletedUsers -TenantId $($customer.TenantID) 15 | foreach($User in $DeletedUsers){ "$($user.UserPrincipalName) has been deleted on $($User.SoftDeletionTimestamp)" } 16 | } 17 | if(!$DeletedUserlist) { $DeletedUserlist = "Healthy" } -------------------------------------------------------------------------------- /Monitoring/O365-JIT-Adminsitrator-Monitoring.ps1: -------------------------------------------------------------------------------- 1 | ############################################################ 2 | $ApplicationId = 'xxxx-xxxx-xxx-xxxx-xxxx' 3 | $ApplicationSecret = 'TheSecretTheSecrey' | Convertto-SecureString -AsPlainText -Force 4 | $TenantID = 'YourTenantID' 5 | $RefreshToken = 'RefreshToken' 6 | $ExchangeRefreshToken = 'ExchangeRefreshToken' 7 | $upn = 'UPN-Used-To-Generate-Tokens' 8 | ############################################################# 9 | $credential = New-Object System.Management.Automation.PSCredential($ApplicationId, $ApplicationSecret) 10 | 11 | $aadGraphToken = New-PartnerAccessToken -ApplicationId $ApplicationId -Credential $credential -RefreshToken $refreshToken -Scopes 'https://graph.windows.net/.default' -ServicePrincipal -Tenant $tenantID 12 | $graphToken = New-PartnerAccessToken -ApplicationId $ApplicationId -Credential $credential -RefreshToken $refreshToken -Scopes 'https://graph.microsoft.com/.default' -ServicePrincipal -Tenant $tenantID 13 | 14 | Connect-MsolService -AdGraphAccessToken $aadGraphToken.AccessToken -MsGraphAccessToken $graphToken.AccessToken 15 | $customers = Get-MsolPartnerContract -All 16 | foreach ($customer in $customers) { 17 | $adminaccounts = (Get-MsolRoleMember -TenantId $customer.tenantid -RoleObjectId (Get-MsolRole -RoleName "Company Administrator").ObjectId).EmailAddress 18 | $token = New-PartnerAccessToken -ApplicationId 'a0c73c16-a7e3-4564-9a95-2bdf47383716'-RefreshToken $ExchangeRefreshToken -Scopes 'https://outlook.office365.com/.default' -Tenant $customer.TenantId 19 | $tokenValue = ConvertTo-SecureString "Bearer $($token.AccessToken)" -AsPlainText -Force 20 | $credential = New-Object System.Management.Automation.PSCredential($upn, $tokenValue) 21 | $customerId = $customer.DefaultDomainName 22 | $session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri "https://ps.outlook.com/powershell-liveid?DelegatedOrg=$($customerId)&BasicAuthToOAuthConversion=true" -Credential $credential -Authentication Basic -AllowRedirection 23 | Import-PSSession $session -allowclobber -DisableNameChecking 24 | $startDate = (Get-Date).AddDays(-1) 25 | $endDate = (Get-Date) 26 | $Logs = @() 27 | Write-Host "Retrieving logs for $($customer.name)" -ForegroundColor Blue 28 | do { 29 | $logs += Search-unifiedAuditLog -SessionCommand ReturnLargeSet -SessionId $customer.name -ResultSize 5000 -StartDate $startDate -EndDate $endDate -Operations UserLoggedIn 30 | Write-Host "Retrieved $($logs.count) logs" -ForegroundColor Yellow 31 | }while ($Logs.count % 5000 -eq 0 -and $logs.count -ne 0) 32 | Write-Host "Finished Retrieving logs" -ForegroundColor Green 33 | $logs | Select-Object UserIds, Operations, CreationDate | Where-Object {$_.UserIds -in $AdminAccounts} 34 | } -------------------------------------------------------------------------------- /Monitoring/O365-Location-Monitoring.ps1: -------------------------------------------------------------------------------- 1 | ######### Secrets ######### 2 | $ApplicationId = 'ApplicationID' 3 | $ApplicationSecret = 'ApplicationSecret' | ConvertTo-SecureString -Force -AsPlainText 4 | $TenantID = 'TenantID' 5 | $RefreshToken = 'VeryLongRefreshToken' 6 | $ExchangeRefreshToken = 'LongExchangeToken' 7 | $UPN = "UPN-User-To-Generate-IDs" 8 | ######### Secrets ######### 9 | 10 | $AllowedCountries = @('Belgium', 'Netherlands', 'Germany', 'United Kingdom') 11 | $Skiplist = @("bla1.onmicrosoft.com", "bla2.onmicrosoft.com", "bla2.onmicrosoft.com") 12 | 13 | $credential = New-Object System.Management.Automation.PSCredential($ApplicationId, $ApplicationSecret) 14 | $aadGraphToken = New-PartnerAccessToken -ApplicationId $ApplicationId -Credential $credential -RefreshToken $refreshToken -Scopes 'https://graph.windows.net/.default' -ServicePrincipal -Tenant $tenantID 15 | $graphToken = New-PartnerAccessToken -ApplicationId $ApplicationId -Credential $credential -RefreshToken $refreshToken -Scopes 'https://graph.microsoft.com/.default' -ServicePrincipal -Tenant $tenantID 16 | Connect-MsolService -AdGraphAccessToken $aadGraphToken.AccessToken -MsGraphAccessToken $graphToken.AccessToken 17 | 18 | $customers = Get-MsolPartnerContract -All | Where-Object { $_.DefaultDomainName -notin $SkipList } 19 | 20 | $StrangeLocations = foreach ($customer in $customers) { 21 | Write-Host "Getting logon location details for $($customer.Name)" -ForegroundColor Green 22 | $token = New-PartnerAccessToken -ApplicationId 'a0c73c16-a7e3-4564-9a95-2bdf47383716'-RefreshToken $ExchangeRefreshToken -Scopes 'https://outlook.office365.com/.default' -Tenant $customer.TenantId 23 | $tokenValue = ConvertTo-SecureString "Bearer $($token.AccessToken)" -AsPlainText -Force 24 | $credential = New-Object System.Management.Automation.PSCredential($upn, $tokenValue) 25 | $customerId = $customer.DefaultDomainName 26 | $session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri "https://ps.outlook.com/powershell-liveid?DelegatedOrg=$($customerId)&BasicAuthToOAuthConversion=true" -Credential $credential -Authentication Basic -AllowRedirection 27 | $null = Import-PSSession $session -CommandName Search-UnifiedAuditLog -AllowClobber 28 | 29 | 30 | $startDate = (Get-Date).AddDays(-1) 31 | $endDate = (Get-Date) 32 | Write-Host "Retrieving logs for $($customer.name)" -ForegroundColor Blue 33 | $logs = do { 34 | $log = Search-unifiedAuditLog -SessionCommand ReturnLargeSet -SessionId $customer.name -ResultSize 5000 -StartDate $startDate -EndDate $endDate -Operations UserLoggedIn 35 | Write-Host "Retrieved $($log.count) logs" -ForegroundColor Yellow 36 | $log 37 | } while ($Log.count % 5000 -eq 0 -and $log.count -ne 0) 38 | Write-Host "Finished Retrieving logs" -ForegroundColor Green 39 | 40 | $userIds = $logs.userIds | Sort-Object -Unique 41 | 42 | $LocationMonitoring = foreach ($userId in $userIds) { 43 | 44 | $searchResult = ($logs | Where-Object { $_.userIds -contains $userId }).auditdata | ConvertFrom-Json -ErrorAction SilentlyContinue 45 | $ips = $searchResult.clientip | Sort-Object -Unique 46 | foreach ($ip in $ips) { 47 | $IsIp = ($ip -as [ipaddress]) -as [bool] 48 | if ($IsIp) { $ipresult = (Invoke-restmethod -method get -uri "https://ip2c.org/$($ip)") -split ';' } 49 | [PSCustomObject]@{ 50 | user = $userId 51 | IP = $ip 52 | Country = ($ipresult | Select-Object -index 3) 53 | CountryCode = ($ipresult | Select-Object -Index 1) 54 | Company = $customer.Name 55 | TenantID = $customer.tenantID 56 | DefaultDomainName = $customer.DefaultDomainName 57 | } 58 | 59 | } 60 | 61 | } 62 | foreach ($Location in $LocationMonitoring) { 63 | if ($Location.country -notin $AllowedCountries) { $Location } 64 | } 65 | } 66 | if (!$StrangeLocations) { 67 | $StrangeLocations = 'Healthy' 68 | } 69 | 70 | $StrangeLocations -------------------------------------------------------------------------------- /Monitoring/O365-NearPermanentDeletedUsers-Monitoring.ps1: -------------------------------------------------------------------------------- 1 | ############################## 2 | $Daystomonitor = (Get-Date).AddDays(-28) #This means we will alert when a user has been deleted for 28 days, and is 1 day before permanent deletion. 3 | ############################## 4 | $ApplicationId = 'XXXX-XXXX-XXXX-XXX-XXX' 5 | $ApplicationSecret = 'YourApplicationSecret' | Convertto-SecureString -AsPlainText -Force 6 | $TenantID = 'YourTenantID.Onmicrosoft.com' 7 | $RefreshToken = 'VeryLongRefreshToken' 8 | ############################## 9 | $credential = New-Object System.Management.Automation.PSCredential($ApplicationId, $ApplicationSecret) 10 | $aadGraphToken = New-PartnerAccessToken -ApplicationId $ApplicationId -Credential $credential -RefreshToken $refreshToken -Scopes 'https://graph.windows.net/.default' -ServicePrincipal -Tenant $tenantID 11 | $graphToken = New-PartnerAccessToken -ApplicationId $ApplicationId -Credential $credential -RefreshToken $refreshToken -Scopes 'https://graph.microsoft.com/.default' -ServicePrincipal -Tenant $tenantID 12 | Connect-MsolService -AdGraphAccessToken $aadGraphToken.AccessToken -MsGraphAccessToken $graphToken.AccessToken 13 | $customers = Get-MsolPartnerContract -All 14 | $DeletedUserlist = foreach ($customer in $customers) { 15 | write-host "Getting Deleted users for $($Customer.name)" -ForegroundColor Green 16 | $DeletedUsers = Get-MsolUser -ReturnDeletedUsers -TenantId $($customer.TenantID) | Where-Object {$($User.SoftDeletionTimestamp) -lt $Daystomonitor} 17 | foreach ($User in $DeletedUsers) { "$($user.UserPrincipalName) has been deleted on $($User.SoftDeletionTimestamp)" } 18 | } 19 | if (!$DeletedUserlist) { $DeletedUserlist= "Healthy" } 20 | 21 | -------------------------------------------------------------------------------- /Monitoring/O365-NoSecureAppModel-MFA-Monitoring.ps1: -------------------------------------------------------------------------------- 1 | Connect-MsolService 2 | $customers = Get-MsolPartnerContract -All | where-object {$_.DefaultDomainName -eq $TenantToCheck} 3 | $MFAType = foreach ($customer in $customers) { 4 | $users = Get-MsolUser -TenantId $customer.tenantid -all 5 | 6 | foreach ($user in $users) { 7 | $primaryMFA = if ($null -ne $user.StrongAuthenticationUserDetails) { ($user.StrongAuthenticationMethods | Where-Object { $_.IsDefault -eq $true }).methodType } else { "MFA Disabled" } 8 | $SecondaryMFA = if ($null -ne $user.StrongAuthenticationUserDetails) { ($user.StrongAuthenticationMethods | Where-Object { $_.IsDefault -eq $false }).methodType } else { "No Secondary Option enabled" } 9 | [PSCustomObject]@{ 10 | "DisplayName" = $user.DisplayName 11 | "user" = $user.UserPrincipalName 12 | "Primary MFA" = $primaryMFA 13 | "Secondary MFA" = $SecondaryMFA 14 | } 15 | } 16 | } 17 | 18 | $UnSafeMFAUsers = $MFAType | Where-Object { $_.'Primary MFA' -like "*SMS*" -or $_.'Primary MFA' -like "*voice*" -or $_.'Primary MFA' -like "*OTP*" } 19 | 20 | if (!$UnSafeMFAUsers) { 21 | $UnSafeMFAUsers = "Healthy" 22 | } -------------------------------------------------------------------------------- /Monitoring/O365-SingleTenant-SharepointSize-Monitoring.ps1: -------------------------------------------------------------------------------- 1 | $ApplicationId = 'YOURAPPLICATIONID' 2 | $ApplicationSecret = 'YOURAPPLICATIONSECRET' | Convertto-SecureString -AsPlainText -Force 3 | $TenantID = 'YOURTENANTID' 4 | $RefreshToken = 'YOURUNBELIEVEBALLYLONGREFRESHTOKEN' 5 | $upn = 'UPN-Used-To-Generate-Tokens' 6 | $TenantToMonitor = "Blabla.onmicrosoft.com" 7 | ############################## 8 | 9 | $LimitsReached = 10 | 11 | write-host "Generating token for $($TenantToMonitor)" -ForegroundColor Green 12 | $graphToken = New-PartnerAccessToken -ApplicationId $ApplicationId -Credential $credential -RefreshToken $refreshToken -Scopes 'https://graph.microsoft.com/.default' -ServicePrincipal -Tenant $TenantToMonitor 13 | $Header = @{ 14 | Authorization = "Bearer $($graphToken.AccessToken)" 15 | } 16 | Write-Host "Grabbing data for $($TenantToMonitor)" -ForegroundColor green 17 | $OneDriveUsageURI = "https://graph.microsoft.com/v1.0/reports/getOneDriveUsageAccountDetail(period='D7')" 18 | $OneDriveUsageReports = (Invoke-RestMethod -Uri $OneDriveUsageURI -Headers $Header -Method Get -ContentType "application/json") | ConvertFrom-Csv 19 | 20 | $SharepointUsageReportsURI = "https://graph.microsoft.com/v1.0/reports/getSharePointSiteUsageDetail(period='D7')" 21 | $SharepointUsageReports = (Invoke-RestMethod -Uri $SharepointUsageReportsURI -Headers $Header -Method Get -ContentType "application/json") | ConvertFrom-Csv 22 | 23 | 24 | foreach ($SharepointReport in $SharepointUsageReports) { 25 | if ([int]$SharepointReport.'File count' -ge [int]"90000") { 26 | $SharepointReport 27 | } 28 | } 29 | 30 | foreach ($OneDriveReport in $OneDriveUsageReports) { 31 | if ([int]$OneDriveReport.'File count' -ge [int]"90000") { 32 | $OneDriveReport 33 | } 34 | } 35 | 36 | 37 | 38 | 39 | if (!$LimitsReached) { 40 | Write-Host "Healthy" -ForegroundColor green 41 | } 42 | else { 43 | Write-Host "Unhealthy" -ForegroundColor Red 44 | $LimitsReached 45 | } -------------------------------------------------------------------------------- /Monitoring/O365-SingleTenants-Account-Age-Monitoring.ps1: -------------------------------------------------------------------------------- 1 | ########################## Secure App Model Settings ############################ 2 | $ApplicationId = 'YourApplicationID' 3 | $ApplicationSecret = 'YourApplicationSecret' | Convertto-SecureString -AsPlainText -Force 4 | $TenantID = 'YourTenantID' 5 | $RefreshToken = 'YourRefreshToken' 6 | $UPN = "YourUPN" 7 | $CustomerTenant = "Customer.onmicrosoft.com" 8 | ########################## Script Settings ############################ 9 | $Date = (get-date).AddDays(-90) 10 | $Baseuri = "https://graph.microsoft.com/beta" 11 | write-host "Generating token to log into Azure AD. Grabbing all tenants" -ForegroundColor Green 12 | $credential = New-Object System.Management.Automation.PSCredential($ApplicationId, $ApplicationSecret) 13 | $CustGraphToken = New-PartnerAccessToken -ApplicationId $ApplicationId -Credential $credential -RefreshToken $refreshToken -Scopes "https://graph.microsoft.com/.default" -ServicePrincipal -Tenant $CustomerTenant 14 | write-host "$($Tenant.Displayname): Starting process." -ForegroundColor Green 15 | $Header = @{ 16 | Authorization = "Bearer $($CustGraphToken.AccessToken)" 17 | } 18 | write-host " $($Tenant.Displayname): Grabbing all Users that have not logged in for 90 days." -ForegroundColor Green 19 | $UserList = (Invoke-RestMethod -Uri "$baseuri/users/?`$select=displayName,UserPrincipalName,signInActivity" -Headers $Header -Method get -ContentType "application/json").value | select-object DisplayName, UserPrincipalName, @{Name = 'LastLogon'; Expression = { [datetime]::Parse($_.SignInActivity.lastSignInDateTime) } } | Where-Object { $_.LastLogon -lt $Date } 20 | $devicesList = (Invoke-RestMethod -Uri "$baseuri/devices" -Headers $Header -Method get -ContentType "application/json").value | select-object Displayname, @{Name = 'LastLogon'; Expression = { [datetime]::Parse($_.approximateLastSignInDateTime) } } 21 | 22 | 23 | $OldObjects = [PSCustomObject]@{ 24 | Users = $UserList | where-object { $_.LastLogon -ne $null } 25 | Devices = $devicesList | Where-Object { $_.LastLogon -lt $Date } 26 | } 27 | 28 | if (!$OldObjects) { write-host "No old objects found in any tenant" } else { write-host "Old objects found."; $Oldobjects } -------------------------------------------------------------------------------- /Monitoring/O365-SingleTenants-LargeMailboxes-Monitoring.ps1: -------------------------------------------------------------------------------- 1 | $ApplicationId = 'xxxx-xxxx-xxx-xxxx-xxxx' 2 | $ApplicationSecret = 'TheSecretTheSecrey' | Convertto-SecureString -AsPlainText -Force 3 | $TenantID = 'YourTenantID' 4 | $RefreshToken = 'RefreshToken' 5 | $ExchangeRefreshToken = 'ExchangeRefreshToken' 6 | $upn = 'UPN-Used-To-Generate-Tokens' 7 | $customertenant = 'CustomerTenant.onmicrosoft.com' 8 | $SizeToMonitor = 60 9 | 10 | $credential = New-Object System.Management.Automation.PSCredential($ApplicationId, $ApplicationSecret) 11 | $aadGraphToken = New-PartnerAccessToken -ApplicationId $ApplicationId -Credential $credential -RefreshToken $refreshToken -Scopes 'https://graph.windows.net/.default' -ServicePrincipal -Tenant $tenantID 12 | $graphToken = New-PartnerAccessToken -ApplicationId $ApplicationId -Credential $credential -RefreshToken $refreshToken -Scopes 'https://graph.microsoft.com/.default' -ServicePrincipal -Tenant $tenantID 13 | Connect-MsolService -AdGraphAccessToken $aadGraphToken.AccessToken -MsGraphAccessToken $graphToken.AccessToken 14 | $LargeMailboxes = @() 15 | 16 | write-host "Getting Large mailboxes" -ForegroundColor green 17 | $token = New-PartnerAccessToken -ApplicationId 'a0c73c16-a7e3-4564-9a95-2bdf47383716'-RefreshToken $ExchangeRefreshToken -Scopes 'https://outlook.office365.com/.default' -Tenant $customertenant 18 | $tokenValue = ConvertTo-SecureString "Bearer $($token.AccessToken)" -AsPlainText -Force 19 | $credential = New-Object System.Management.Automation.PSCredential($upn, $tokenValue) 20 | $customerId = $customertenant 21 | $session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri "https://ps.outlook.com/powershell-liveid?DelegatedOrg=$($customerId)&BasicAuthToOAuthConversion=true" -Credential $credential -Authentication Basic -AllowRedirection 22 | Import-PSSession $session -allowclobber -Disablenamechecking 23 | $Mailboxes = Get-Mailbox | Get-MailboxStatistics | Select-Object DisplayName, @{name = "TotalItemSize (GB)"; expression = { [math]::Round((($_.TotalItemSize.Value.ToString()).Split("(")[1].Split(" ")[0].Replace(",", "") / 1GB), 2) } }, ItemCount | Sort "TotalItemSize (GB)" -Descending 24 | foreach ($Mailbox in $Mailboxes) { if ($Mailbox.'TotalItemSize (GB)' -gt $SizeToMonitor { $LargeMailboxes += $Mailbox } } 25 | Remove-PSSession $session 26 | 27 | if (!$LargeMailboxes) { "No Large mailboxes found" } -------------------------------------------------------------------------------- /Monitoring/O365-UnusedLicense-Monitoring.ps1: -------------------------------------------------------------------------------- 1 | ############################## 2 | $ApplicationId = 'XXXX-XXXX-XXXX-XXX-XXX' 3 | $ApplicationSecret = 'YourApplicationSecret' | Convertto-SecureString -AsPlainText -Force 4 | $TenantID = 'YourTenantID.Onmicrosoft.com' 5 | $RefreshToken = 'VeryLongRefreshToken' 6 | ############################## 7 | $credential = New-Object System.Management.Automation.PSCredential($ApplicationId, $ApplicationSecret) 8 | $aadGraphToken = New-PartnerAccessToken -ApplicationId $ApplicationId -Credential $credential -RefreshToken $refreshToken -Scopes 'https://graph.windows.net/.default' -ServicePrincipal -Tenant $tenantID 9 | $graphToken = New-PartnerAccessToken -ApplicationId $ApplicationId -Credential $credential -RefreshToken $refreshToken -Scopes 'https://graph.microsoft.com/.default' -ServicePrincipal -Tenant $tenantID 10 | Connect-MsolService -AdGraphAccessToken $aadGraphToken.AccessToken -MsGraphAccessToken $graphToken.AccessToken 11 | $customers = Get-MsolPartnerContract -All 12 | $UnusedLicensesList = foreach ($customer in $customers) { 13 | write-host "Getting licenses $($customer.name)" -ForegroundColor Green 14 | $Licenses = Get-MsolAccountSku -TenantId $($customer.TenantId) 15 | foreach ($License in $Licenses) { 16 | if ($License.ActiveUnits -lt $License.consumedUnits) { "$($customer.name) - $($License.AccountSkuId) has licenses available." } 17 | 18 | } 19 | } 20 | if (!$UnusedLicensesList) { $UnusedLicensesList = "Healthy" } -------------------------------------------------------------------------------- /Monitoring/O365-singleTenant-MFA-Monitoring.ps1: -------------------------------------------------------------------------------- 1 | $ApplicationId = 'xxxx-xxxx-xxxx-xxxx-xxx' 2 | $ApplicationSecret = 'YOURSECRET' | Convertto-SecureString -AsPlainText -Force 3 | $TenantID = 'xxxxxx-xxxx-xxx-xxxx--xxx' 4 | $RefreshToken = 'LongResourcetoken' 5 | $ExchangeRefreshToken = 'LongExchangeToken' 6 | $TenantToCheck = 'tenant.onmicrosoft.com' 7 | $credential = New-Object System.Management.Automation.PSCredential($ApplicationId, $ApplicationSecret) 8 | $aadGraphToken = New-PartnerAccessToken -ApplicationId $ApplicationId -Credential $credential -RefreshToken $refreshToken -Scopes 'https://graph.windows.net/.default' -ServicePrincipal -Tenant $tenantID 9 | $graphToken = New-PartnerAccessToken -ApplicationId $ApplicationId -Credential $credential -RefreshToken $refreshToken -Scopes 'https://graph.microsoft.com/.default' -ServicePrincipal -Tenant $tenantID 10 | 11 | Connect-MsolService -AdGraphAccessToken $aadGraphToken.AccessToken -MsGraphAccessToken $graphToken.AccessToken 12 | $customers = Get-MsolPartnerContract -All | where-object {$_.DefaultDomainName -eq $TenantToCheck} 13 | $MFAType = foreach ($customer in $customers) { 14 | $users = Get-MsolUser -TenantId $customer.tenantid -all 15 | 16 | foreach ($user in $users) { 17 | $primaryMFA = if ($null -ne $user.StrongAuthenticationUserDetails) { ($user.StrongAuthenticationMethods | Where-Object { $_.IsDefault -eq $true }).methodType } else { "MFA Disabled" } 18 | $SecondaryMFA = if ($null -ne $user.StrongAuthenticationUserDetails) { ($user.StrongAuthenticationMethods | Where-Object { $_.IsDefault -eq $false }).methodType } else { "No Secondary Option enabled" } 19 | [PSCustomObject]@{ 20 | "DisplayName" = $user.DisplayName 21 | "user" = $user.UserPrincipalName 22 | "Primary MFA" = $primaryMFA 23 | "Secondary MFA" = $SecondaryMFA 24 | } 25 | } 26 | } 27 | 28 | $UnSafeMFAUsers = $MFAType | Where-Object { $_.'Primary MFA' -like "*SMS*" -or $_.'Primary MFA' -like "*voice*" -or $_.'Primary MFA' -like "*OTP*" } 29 | 30 | if (!$UnSafeMFAUsers) { 31 | $UnSafeMFAUsers = "Healthy" 32 | } -------------------------------------------------------------------------------- /Monitoring/SQL-ServerHealth-Monitoring.ps1: -------------------------------------------------------------------------------- 1 | import-module SQLPS 2 | $Instances = Get-ChildItem "SQLSERVER:\SQL\$($ENV:COMPUTERNAME)" 3 | foreach ($Instance in $Instances) { 4 | $databaseList = get-childitem "SQLSERVER:\SQL\$($ENV:COMPUTERNAME)\$($Instance.Displayname)\Databases" 5 | $SkipDatabases = @("Master","Model","ReportServer","SLDModel.SLDData") 6 | $Errors = foreach ($Database in $databaselist | Where-Object {$_.Name -notin $SkipDatabases}) { 7 | if ($Database.status -ne "normal") {"$($Database.name) has the status: $($Database.status)" } 8 | if ($Database.RecoveryModel -ne "Simple") { "$($Database.name) is in logging mode $($Database.RecoveryModel)" } 9 | if ($database.filegroups.files.MaxSize -ne "-1") { "$($Database.name) has a Max Size set." } 10 | if ($database.filegroups.files.filename -contains "C:") { "$($Database.name) is located on the C:\ drive." } 11 | } 12 | } 13 | if (!$errors) { $HealthState = "Healthy" } else { $HealthState = $Errors } -------------------------------------------------------------------------------- /Monitoring/Server-Cert-Bindings-Monitor.ps1: -------------------------------------------------------------------------------- 1 | $days = (Get-Date).AddDays(14) 2 | $TxtBindings = (& netsh http show sslcert) | select-object -skip 3 | out-string 3 | $nl = [System.Environment]::NewLine 4 | $Txtbindings = $TxtBindings -split "$nl$nl" 5 | $BindingsList = foreach ($Binding in $Txtbindings) { 6 | if ($Binding -ne "") { 7 | $Binding = $Binding -replace " ", "" -split ": " 8 | [pscustomobject]@{ 9 | IPPort = ($Binding[1] -split "`n")[0] 10 | CertificateHash = ($Binding[2] -split "`n" -replace '[^a-zA-Z0-9]', '')[0] 11 | AppID = ($Binding[3] -split "`n")[0] 12 | CertStore = ($Binding[4] -split "`n")[0] 13 | } 14 | } 15 | } 16 | 17 | if ($BindingsList.Count -eq 0) { 18 | $CertState = "Healthy - No certificate bindings found." 19 | exit 0 20 | } 21 | 22 | $CertState = foreach ($bind in $bindingslist) { 23 | $CertFile = Get-ChildItem -path "CERT:LocalMachine\MY" | Where-Object -Property ThumbPrint -eq $bind.CertificateHash 24 | if ($certfile.NotAfter -lt $Days) { "$($certfile.FriendlyName) / $($certfile.thumbprint) will expire on $($certfile.NotAfter)" } 25 | } -------------------------------------------------------------------------------- /Monitoring/Server-ComputerAge-Monitoring.ps1: -------------------------------------------------------------------------------- 1 | $ENV:ComputerAge = 90 2 | $age = (get-date).AddDays(-$ENV:ComputerAge) 3 | $DomainCheck = Get-CimInstance -ClassName Win32_OperatingSystem 4 | if ($DomainCheck.ProductType -ne "2") { write-host "Not a domain controller. Soft exiting." ; exit 0 } 5 | $OldComputers = Get-ADComputer -Filter * -properties DNSHostName,Enabled,WhenCreated,LastLogonDate | select DNSHostName,Enabled,WhenCreated,LastLogonDate | Where-Object {$_.LastLogonDate -lt $age} 6 | 7 | 8 | if (!$OldComputers) { 9 | write-host "Healthy - No computers older than $ENV:ComputerAge found." 10 | } 11 | else { 12 | write-host"Not Healthy - Computer accounts found older than $ENV:ComputerAge days" 13 | write-host @($OldComputers) 14 | } -------------------------------------------------------------------------------- /Monitoring/Server-DFSR-Monitoring.ps1: -------------------------------------------------------------------------------- 1 | $MaxBackLog = "100" 2 | $DFSFiles = Get-DfsrState 3 | $DFSBackLogHealth = if ($DFSFiles.count -gt $Maxbacklog) { "There are more than $Maxbacklog in the backlog" } else { "Healthy" } 4 | 5 | $connections = Get-DfsrConnection | Where-Object {$_.state -ne 'normal'} 6 | $DFSConnectionHealth = if($Connections) { "Fault connections found. Please investigate" } else { "Healthy" } 7 | 8 | 9 | $Folders = Get-DfsReplicatedFolder | Where-Object {$_.state -ne 'normal'} 10 | $DFSFolderHealth = if($Folders) { "Faulty folder found. Please investigate" } else { "Healthy" } -------------------------------------------------------------------------------- /Monitoring/Server-RogueDHCP-Monitoring.ps1: -------------------------------------------------------------------------------- 1 | $AllowedDHCPServer = "192.168.15.1" 2 | 3 | #Replace the Download URL to where you've uploaded the DHCPTest file yourself. We will only download this file once. 4 | $DownloadURL = "https://cyberdrain.com/wp-content/uploads/2020/04/dhcptest-0.7-win64.exe" 5 | $DownloadLocation = "$($Env:ProgramData)\DHCPTest" 6 | try { 7 | $TestDownloadLocation = Test-Path $DownloadLocation 8 | if (!$TestDownloadLocation) { new-item $DownloadLocation -ItemType Directory -force } 9 | $TestDownloadLocationZip = Test-Path "$DownloadLocation\DHCPTest.exe" 10 | if (!$TestDownloadLocationZip) { Invoke-WebRequest -UseBasicParsing -Uri $DownloadURL -OutFile "$($DownloadLocation)\DHCPTest.exe" } 11 | } 12 | catch { 13 | write-host "The download and extraction of DHCPTest failed. Error: $($_.Exception.Message)" 14 | exit 1 15 | } 16 | $Tests = 0 17 | $ListedDHCPServers = do { 18 | & "$DownloadLocation\DHCPTest.exe" --quiet --query --print-only 54 --wait --timeout 3 19 | $Tests ++ 20 | } while ($Tests -lt 2) 21 | 22 | $DHCPHealth = foreach ($ListedServer in $ListedDHCPServers) { 23 | if ($ListedServer -ne $AllowedDHCPServer) { "Rogue DHCP Server found. IP of rogue server is $ListedServer" } 24 | } 25 | 26 | if (!$DHCPHealth) { $DHCPHealth = "Healthy. No Rogue DHCP servers found." } -------------------------------------------------------------------------------- /Monitoring/Server-Userage-Monitoring.ps1: -------------------------------------------------------------------------------- 1 | $ENV:UserAge = 30 2 | $age = (get-date).AddDays(-$ENV:UserAge) 3 | $DomainCheck = Get-CimInstance -ClassName Win32_OperatingSystem 4 | if ($DomainCheck.ProductType -ne "2") { write-host "Not a domain controller. Soft exiting." ; exit 0 } 5 | $OldUsers = Get-ADuser-Filter * -properties UserPrincipalName, Enabled, WhenCreated, LastLogonDate | select UserPrincipalName, Enabled, WhenCreated, LastLogonDate | Where-Object { $_.LastLogonDate -lt $age } 6 | 7 | 8 | if (!$OldUsers) { 9 | write-host "Healthy" 10 | } 11 | else { 12 | write-host "Not Healthy - Users found that havent logged in for $ENV:UserAge days" 13 | write-host @($OldUsers) 14 | } -------------------------------------------------------------------------------- /Monitoring/Server-VSSSnapshotSize.ps1: -------------------------------------------------------------------------------- 1 | $threshold = "600" #threshold in GB 2 | $DiskSpaceUsed = Get-CimInstance -ClassName Win32_ShadowStorage | Select-Object @{n = "Used (GB)"; e = { [math]::Round([double]$_.UsedSpace / 1GB, 3) } }, @{n = "Max (GB)"; e = { [math]::Round([double]$_.MAxSpace / 1GB, 3) } }, * 3 | $HealthState = foreach ($Disks in $DiskSpaceUsed) { 4 | 5 | $Volume = get-volume -UniqueId $DiskSpaceUsed.Volume.DeviceID 6 | $DiskSize = [math]::Round([double]$volume.Size / 1GB, 3) 7 | $diskremaining = [math]::Round([double]$volume.SizeRemaining / 1GB, 3) 8 | if ($Disks.'Used (GB)' -gt $threshold) { "Disk $($Volume.DriveLetter) snapshot size is higher than $Threshold. The disk size is $($diskSize) and it has $($diskremaining) remaining space. The max snapshot size is $($Disks.'Max (GB)')" } 9 | } 10 | 11 | if (!$HealthState) { 12 | $HealthState = "Healthy" 13 | } -------------------------------------------------------------------------------- /Monitoring/Shodan-Monitoring.ps1: -------------------------------------------------------------------------------- 1 | $APIKEY = "YourShodanAPIKey" 2 | $CurrentIP = (Invoke-WebRequest -uri "http://ifconfig.me/ip" -UseBasicParsing ).Content 3 | $ListIPs = @("1.1.1.1","2.2.2.2",$CurrentIP) 4 | foreach($ip in $ListIPs){ 5 | $Shodan = Invoke-RestMethod -uri "https://api.shodan.io/shodan/host/$($ip)?key=$APIKEY" 6 | } 7 | if(!$Shodan) { $HealthState = "Healthy"} else { $HealthState = "Alert - $($Shodan.ip_str) is found in Shodan."} -------------------------------------------------------------------------------- /Monitoring/Unifi-Settings-Monitoring.ps1: -------------------------------------------------------------------------------- 1 | param( 2 | [string]$URL = 'yourcontroller.controller.tld', 3 | [string]$port = '8443', 4 | [string]$User = 'APIUSER', 5 | [string]$Pass = 'SomeReallyLongPassword', 6 | [string]$SiteCode = 'default' #you can enter each site here. This way when you assign the monitoring to a client you edit this to match the correct siteID. 7 | ) 8 | [string]$controller = "https://$($URL):$($port)" 9 | [string]$credential = "`{`"username`":`"$User`",`"password`":`"$Pass`"`}" 10 | 11 | 12 | $errorlist = New-Object -TypeName PSCustomObject 13 | try { 14 | $null = Invoke-Restmethod -Uri "$controller/api/login" -method post -body $credential -ContentType "application/json; charset=utf-8" -SessionVariable myWebSession 15 | } 16 | catch { 17 | Add-Member -InputObject $ErrorList -MemberType NoteProperty -Name APISessionError -Value $_.Exception.Message 18 | } 19 | 20 | try { 21 | $NetWorkConf = (Invoke-Restmethod -Uri "$controller/api/s/$SiteCode/list/networkconf" -WebSession $myWebSession).data | Where-Object { $_.Purpose -ne "WAN" } 22 | } 23 | catch { 24 | Add-Member -InputObject $ErrorList -MemberType NoteProperty -Name APINetworkError -Value $_.Exception.Message 25 | } 26 | 27 | try { 28 | $SysInfo = (Invoke-Restmethod -Uri "$controller/api/s/$SiteCode/get/setting" -WebSession $myWebSession).data 29 | } 30 | catch { 31 | Add-Member -InputObject $ErrorList -MemberType NoteProperty -Name APISysInfoError -Value $_.Exception.Message 32 | } 33 | 34 | $UnifiOutput = [PSCustomObject]@{ 35 | NetworkNames = $Networkconf.name 36 | NetworkCount = $NetWorkConf.Count 37 | AdvancedFeatures = ($Sysinfo.advanced_feature_enabled) 38 | SpeedTestEnabled = ($sysinfo | Where-Object { $_.key -eq "Auto_Speedtest" }).enabled 39 | SpeedTestInterval = ($sysinfo | Where-Object { $_.key -eq "Auto_Speedtest" }).interval 40 | VoipNetwork = ($NetWorkConf.name | Where-Object { $_ -like "*VOIP*" }).Count 41 | GuestNetwork = ($NetWorkConf.purpose | Where-Object { $_ -like "*guest*" }).Count 42 | LANNetworks = ($NetWorkConf.name | Where-Object { $_ -like "*-LAN*" }).Count 43 | Modules = [PSCustomObject]@{ 44 | ftp_module = $sysinfo.ftp_module 45 | gre_module = $sysinfo.gre_module 46 | h323_module = $sysinfo.h323_module 47 | pptp_module = $sysinfo.pptp_module 48 | sip_module = $sysinfo.sip_module 49 | tftp_module = $sysinfo.tftp_module 50 | broadcast_ping = $sysinfo.broadcast_ping 51 | receive_redirects = $sysinfo.receive_redirects 52 | send_redirects = $sysinfo.send_redirects 53 | syn_cookies = $sysinfo.syn_cookies 54 | offload_accounting = $sysinfo.offload_accounting 55 | offload_sch = $sysinfo.offload_sch 56 | offload_l2_blocking = $sysinfo.offload_l2_blocking 57 | mdns_enabled = $sysinfo.mdns_enabled 58 | upnp_enabled = $sysinfo.upnp_enabled 59 | upnp_nat_pmp_enabled = $sysinfo.upnp_nat_pmp_enabled 60 | upnp_secure_mode = $sysinfo.upnp_secure_mode 61 | mss_clamp = $sysinfo.mss_clamp 62 | } 63 | } 64 | 65 | if ($UnifiOutput.NetworkCount -lt "3") { write-host "Not enough networks found. Only 3 are present." } 66 | if ($UnifiOutput.SpeedTestEnabled -eq $false) { write-host "Speedtest disabled" } 67 | if ($UnifiOutput.SpeedTestInterval -gt "20") { write-host "Speedtest is not set to run every 20 minutes." } 68 | if ($UnifiOutput.SpeedTestInterval -gt "20") { write-host "Speedtest is not set to run every 20 minutes." } 69 | if ($UnifiOutput.Modules.sip_module -eq $true) { Write-Host "SIP ALG Module is enabled." } -------------------------------------------------------------------------------- /Monitoring/W10-ActiveSMB-Monitoring.ps1: -------------------------------------------------------------------------------- 1 | $Sessions = Get-smbsession 2 | $Connections = get-smbconnection 3 | 4 | 5 | if ($sessions) { 6 | foreach ($Session in $Sessions) { 7 | write-host "a session has been found coming from $($Session.ClientComputerName). The logged on user is $($Session.ClientUserName) with $($Session.NumOpens) opened sessions" 8 | } 9 | } 10 | else { 11 | write-host "No sessions found" 12 | } 13 | 14 | if ($Connections) { 15 | foreach ($Connection in $Connections) { 16 | write-host "a Connection has been found on $($Connection.ServerName). The logged on user is $($Connection.Username) with $($Connection.NumOpens) opened sessions" 17 | } 18 | } 19 | else { 20 | write-host "No sessions found" 21 | } -------------------------------------------------------------------------------- /Monitoring/W10-ClientBasedVPN-Monitoring.ps1: -------------------------------------------------------------------------------- 1 | $Settings = @{ 2 | name = "Client based VPN" 3 | alluserconnection = $true 4 | ServerAddress = "remote.clientname.com" 5 | TunnelType = "SSTP" #Can be: Automatic, Ikev2m L2TP, PPTP,SSTP. 6 | SplitTunneling = $True 7 | UseWinLogonCredential = $true 8 | #There's a lot more options to set/monitor. Investigate for your own settings. 9 | } 10 | $VPN = Get-VPNconnection -name $($Settings.name) -AllUserConnection -ErrorAction SilentlyContinue 11 | if (!$VPN) { 12 | $VPNHealth = "Unhealthy - Could not find VPN Connection." 13 | } 14 | else { 15 | $ExpectedVPNSettings = New-Object PSCustomObject -property $Settings 16 | $Selection = $propsToCompare = $ExpectedVPNSettings.psobject.properties.name 17 | $CurrentVPNSettings = $VPN | Select-object $Selection 18 | $CompareVPNSettings = compare-object $CurrentVPNSettings $ExpectedVPNSettings -Property $Selection 19 | if (!$CompareVPNSettings) { $VPNHealth = "Healthy" } else { $VPNHealth = "Unhealthy - Settings do not match." } 20 | } -------------------------------------------------------------------------------- /Monitoring/W10-GPODEployment-Monitoring.ps1: -------------------------------------------------------------------------------- 1 | $GPOFile = "C:\ProgramData\GPO 1.0 User.log" 2 | 3 | $Check = Test-Path "C:\ProgramData\GPO 1.0 User.log" 4 | if (!$Check) { 5 | $Healthstate = "GPO has not deployed. Log file does not exist." 6 | } 7 | else { 8 | $State = get-content $GPOFile | Select-Object -last 3 9 | if ($state[0] -ne "POLICY SAVED.") { $Healthstate = "GPO Log found but policy not saved." } else { $Healthstate = "Healthy" } 10 | } -------------------------------------------------------------------------------- /Monitoring/W10-OneDriveKFM-Monitoring.ps1: -------------------------------------------------------------------------------- 1 | $Source = @" 2 | using System; 3 | using System.Runtime.InteropServices; 4 | 5 | namespace murrayju.ProcessExtensions 6 | { 7 | public static class ProcessExtensions 8 | { 9 | #region Win32 Constants 10 | 11 | private const int CREATE_UNICODE_ENVIRONMENT = 0x00000400; 12 | private const int CREATE_NO_WINDOW = 0x08000000; 13 | 14 | private const int CREATE_NEW_CONSOLE = 0x00000010; 15 | 16 | private const uint INVALID_SESSION_ID = 0xFFFFFFFF; 17 | private static readonly IntPtr WTS_CURRENT_SERVER_HANDLE = IntPtr.Zero; 18 | 19 | #endregion 20 | 21 | #region DllImports 22 | 23 | [DllImport("advapi32.dll", EntryPoint = "CreateProcessAsUser", SetLastError = true, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)] 24 | private static extern bool CreateProcessAsUser( 25 | IntPtr hToken, 26 | String lpApplicationName, 27 | String lpCommandLine, 28 | IntPtr lpProcessAttributes, 29 | IntPtr lpThreadAttributes, 30 | bool bInheritHandle, 31 | uint dwCreationFlags, 32 | IntPtr lpEnvironment, 33 | String lpCurrentDirectory, 34 | ref STARTUPINFO lpStartupInfo, 35 | out PROCESS_INFORMATION lpProcessInformation); 36 | 37 | [DllImport("advapi32.dll", EntryPoint = "DuplicateTokenEx")] 38 | private static extern bool DuplicateTokenEx( 39 | IntPtr ExistingTokenHandle, 40 | uint dwDesiredAccess, 41 | IntPtr lpThreadAttributes, 42 | int TokenType, 43 | int ImpersonationLevel, 44 | ref IntPtr DuplicateTokenHandle); 45 | 46 | [DllImport("userenv.dll", SetLastError = true)] 47 | private static extern bool CreateEnvironmentBlock(ref IntPtr lpEnvironment, IntPtr hToken, bool bInherit); 48 | 49 | [DllImport("userenv.dll", SetLastError = true)] 50 | [return: MarshalAs(UnmanagedType.Bool)] 51 | private static extern bool DestroyEnvironmentBlock(IntPtr lpEnvironment); 52 | 53 | [DllImport("kernel32.dll", SetLastError = true)] 54 | private static extern bool CloseHandle(IntPtr hSnapshot); 55 | 56 | [DllImport("kernel32.dll")] 57 | private static extern uint WTSGetActiveConsoleSessionId(); 58 | 59 | [DllImport("Wtsapi32.dll")] 60 | private static extern uint WTSQueryUserToken(uint SessionId, ref IntPtr phToken); 61 | 62 | [DllImport("wtsapi32.dll", SetLastError = true)] 63 | private static extern int WTSEnumerateSessions( 64 | IntPtr hServer, 65 | int Reserved, 66 | int Version, 67 | ref IntPtr ppSessionInfo, 68 | ref int pCount); 69 | 70 | #endregion 71 | 72 | #region Win32 Structs 73 | 74 | private enum SW 75 | { 76 | SW_HIDE = 0, 77 | SW_SHOWNORMAL = 1, 78 | SW_NORMAL = 1, 79 | SW_SHOWMINIMIZED = 2, 80 | SW_SHOWMAXIMIZED = 3, 81 | SW_MAXIMIZE = 3, 82 | SW_SHOWNOACTIVATE = 4, 83 | SW_SHOW = 5, 84 | SW_MINIMIZE = 6, 85 | SW_SHOWMINNOACTIVE = 7, 86 | SW_SHOWNA = 8, 87 | SW_RESTORE = 9, 88 | SW_SHOWDEFAULT = 10, 89 | SW_MAX = 10 90 | } 91 | 92 | private enum WTS_CONNECTSTATE_CLASS 93 | { 94 | WTSActive, 95 | WTSConnected, 96 | WTSConnectQuery, 97 | WTSShadow, 98 | WTSDisconnected, 99 | WTSIdle, 100 | WTSListen, 101 | WTSReset, 102 | WTSDown, 103 | WTSInit 104 | } 105 | 106 | [StructLayout(LayoutKind.Sequential)] 107 | private struct PROCESS_INFORMATION 108 | { 109 | public IntPtr hProcess; 110 | public IntPtr hThread; 111 | public uint dwProcessId; 112 | public uint dwThreadId; 113 | } 114 | 115 | private enum SECURITY_IMPERSONATION_LEVEL 116 | { 117 | SecurityAnonymous = 0, 118 | SecurityIdentification = 1, 119 | SecurityImpersonation = 2, 120 | SecurityDelegation = 3, 121 | } 122 | 123 | [StructLayout(LayoutKind.Sequential)] 124 | private struct STARTUPINFO 125 | { 126 | public int cb; 127 | public String lpReserved; 128 | public String lpDesktop; 129 | public String lpTitle; 130 | public uint dwX; 131 | public uint dwY; 132 | public uint dwXSize; 133 | public uint dwYSize; 134 | public uint dwXCountChars; 135 | public uint dwYCountChars; 136 | public uint dwFillAttribute; 137 | public uint dwFlags; 138 | public short wShowWindow; 139 | public short cbReserved2; 140 | public IntPtr lpReserved2; 141 | public IntPtr hStdInput; 142 | public IntPtr hStdOutput; 143 | public IntPtr hStdError; 144 | } 145 | 146 | private enum TOKEN_TYPE 147 | { 148 | TokenPrimary = 1, 149 | TokenImpersonation = 2 150 | } 151 | 152 | [StructLayout(LayoutKind.Sequential)] 153 | private struct WTS_SESSION_INFO 154 | { 155 | public readonly UInt32 SessionID; 156 | 157 | [MarshalAs(UnmanagedType.LPStr)] 158 | public readonly String pWinStationName; 159 | 160 | public readonly WTS_CONNECTSTATE_CLASS State; 161 | } 162 | 163 | #endregion 164 | 165 | // Gets the user token from the currently active session 166 | private static bool GetSessionUserToken(ref IntPtr phUserToken) 167 | { 168 | var bResult = false; 169 | var hImpersonationToken = IntPtr.Zero; 170 | var activeSessionId = INVALID_SESSION_ID; 171 | var pSessionInfo = IntPtr.Zero; 172 | var sessionCount = 0; 173 | 174 | // Get a handle to the user access token for the current active session. 175 | if (WTSEnumerateSessions(WTS_CURRENT_SERVER_HANDLE, 0, 1, ref pSessionInfo, ref sessionCount) != 0) 176 | { 177 | var arrayElementSize = Marshal.SizeOf(typeof(WTS_SESSION_INFO)); 178 | var current = pSessionInfo; 179 | 180 | for (var i = 0; i < sessionCount; i++) 181 | { 182 | var si = (WTS_SESSION_INFO)Marshal.PtrToStructure((IntPtr)current, typeof(WTS_SESSION_INFO)); 183 | current += arrayElementSize; 184 | 185 | if (si.State == WTS_CONNECTSTATE_CLASS.WTSActive) 186 | { 187 | activeSessionId = si.SessionID; 188 | } 189 | } 190 | } 191 | 192 | // If enumerating did not work, fall back to the old method 193 | if (activeSessionId == INVALID_SESSION_ID) 194 | { 195 | activeSessionId = WTSGetActiveConsoleSessionId(); 196 | } 197 | 198 | if (WTSQueryUserToken(activeSessionId, ref hImpersonationToken) != 0) 199 | { 200 | // Convert the impersonation token to a primary token 201 | bResult = DuplicateTokenEx(hImpersonationToken, 0, IntPtr.Zero, 202 | (int)SECURITY_IMPERSONATION_LEVEL.SecurityImpersonation, (int)TOKEN_TYPE.TokenPrimary, 203 | ref phUserToken); 204 | 205 | CloseHandle(hImpersonationToken); 206 | } 207 | 208 | return bResult; 209 | } 210 | 211 | public static bool StartProcessAsCurrentUser(string appPath, string cmdLine = null, string workDir = null, bool visible = true) 212 | { 213 | var hUserToken = IntPtr.Zero; 214 | var startInfo = new STARTUPINFO(); 215 | var procInfo = new PROCESS_INFORMATION(); 216 | var pEnv = IntPtr.Zero; 217 | int iResultOfCreateProcessAsUser; 218 | 219 | startInfo.cb = Marshal.SizeOf(typeof(STARTUPINFO)); 220 | 221 | try 222 | { 223 | if (!GetSessionUserToken(ref hUserToken)) 224 | { 225 | throw new Exception("StartProcessAsCurrentUser: GetSessionUserToken failed."); 226 | } 227 | 228 | uint dwCreationFlags = CREATE_UNICODE_ENVIRONMENT | (uint)(visible ? CREATE_NEW_CONSOLE : CREATE_NO_WINDOW); 229 | startInfo.wShowWindow = (short)(visible ? SW.SW_SHOW : SW.SW_HIDE); 230 | startInfo.lpDesktop = "winsta0\\default"; 231 | 232 | if (!CreateEnvironmentBlock(ref pEnv, hUserToken, false)) 233 | { 234 | throw new Exception("StartProcessAsCurrentUser: CreateEnvironmentBlock failed."); 235 | } 236 | 237 | if (!CreateProcessAsUser(hUserToken, 238 | appPath, // Application Name 239 | cmdLine, // Command Line 240 | IntPtr.Zero, 241 | IntPtr.Zero, 242 | false, 243 | dwCreationFlags, 244 | pEnv, 245 | workDir, // Working directory 246 | ref startInfo, 247 | out procInfo)) 248 | { 249 | throw new Exception("StartProcessAsCurrentUser: CreateProcessAsUser failed.\n"); 250 | } 251 | 252 | iResultOfCreateProcessAsUser = Marshal.GetLastWin32Error(); 253 | } 254 | finally 255 | { 256 | CloseHandle(hUserToken); 257 | if (pEnv != IntPtr.Zero) 258 | { 259 | DestroyEnvironmentBlock(pEnv); 260 | } 261 | CloseHandle(procInfo.hThread); 262 | CloseHandle(procInfo.hProcess); 263 | } 264 | return true; 265 | } 266 | } 267 | } 268 | 269 | 270 | "@ 271 | 272 | New-Item "C:\programdata\Microsoft OneDrive" -ItemType directory -Force -ErrorAction SilentlyContinue 273 | Invoke-WebRequest -Uri 'https://raw.githubusercontent.com/rodneyviana/ODSyncService/master/Binaries/PowerShell/OneDriveLib.dll' -OutFile 'C:\programdata\Microsoft OneDrive\OneDriveLib.dll' 274 | 275 | Add-Type -ReferencedAssemblies 'System', 'System.Runtime.InteropServices' -TypeDefinition $Source -Language CSharp 276 | $scriptblock = { 277 | $KFMSet = get-ItemProperty -Path "HKLM:\SOFTWARE\Policies\Microsoft\OneDrive" -Name "KFMSilentOptIn" 278 | if (!$KFMSet) { 279 | $KFMHealth = "KFM has not been set via registry" 280 | } 281 | else { 282 | $Desktop = (Get-ItemProperty -path "HKCU:\Software\Microsoft\Windows\CurrentVersion\Explorer\User Shell Folders" -name "Desktop").Desktop 283 | $Pictures = (Get-ItemProperty -path "HKCU:\Software\Microsoft\Windows\CurrentVersion\Explorer\User Shell Folders" -name "My Pictures").'My pictures' 284 | $Documents = (Get-ItemProperty -path "HKCU:\Software\Microsoft\Windows\CurrentVersion\Explorer\User Shell Folders" -name "Personal").Personal 285 | 286 | if($Desktop -notlike "$($ENV:OneDrive)*") { $KFMHealth = "Desktop is not set to Onedrive location." } 287 | if($Pictures -notlike "$($ENV:OneDrive)*") { $KFMHealth = "Pictures is not set to Onedrive location." } 288 | if($Documents -notlike "$($ENV:OneDrive)*") { $KFMHealth = "My Documents is not set to Onedrive location." } 289 | } 290 | if($KFMHealth -eq $null) { $KFMHealth = "Healthy" } 291 | $KFMHealth | Out-File "C:\programdata\Microsoft OneDrive\OneDriveKFMLogging.txt" -Force 292 | } 293 | 294 | $bytes = [System.Text.Encoding]::Unicode.GetBytes($scriptblock) 295 | $encodedCommand = [Convert]::ToBase64String($bytes) 296 | 297 | [murrayju.ProcessExtensions.ProcessExtensions]::StartProcessAsCurrentUser("C:\Windows\System32\WindowsPowershell\v1.0\Powershell.exe", " -Encodedcommand $encodedCommand","C:\Windows\System32\WindowsPowershell\v1.0",$false) 298 | start-sleep 2 299 | $KFMHealth = get-content 'C:\programdata\Microsoft OneDrive\OneDriveKFMLogging.txt' 300 | if($KFMHealth -eq $null) { $KFMHealth = "Script failed." } -------------------------------------------------------------------------------- /Monitoring/W10-OneDriveSyncStatus-Monitoring.ps1: -------------------------------------------------------------------------------- 1 | $Source = @" 2 | using System; 3 | using System.Runtime.InteropServices; 4 | 5 | namespace murrayju.ProcessExtensions 6 | { 7 | public static class ProcessExtensions 8 | { 9 | #region Win32 Constants 10 | 11 | private const int CREATE_UNICODE_ENVIRONMENT = 0x00000400; 12 | private const int CREATE_NO_WINDOW = 0x08000000; 13 | 14 | private const int CREATE_NEW_CONSOLE = 0x00000010; 15 | 16 | private const uint INVALID_SESSION_ID = 0xFFFFFFFF; 17 | private static readonly IntPtr WTS_CURRENT_SERVER_HANDLE = IntPtr.Zero; 18 | 19 | #endregion 20 | 21 | #region DllImports 22 | 23 | [DllImport("advapi32.dll", EntryPoint = "CreateProcessAsUser", SetLastError = true, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)] 24 | private static extern bool CreateProcessAsUser( 25 | IntPtr hToken, 26 | String lpApplicationName, 27 | String lpCommandLine, 28 | IntPtr lpProcessAttributes, 29 | IntPtr lpThreadAttributes, 30 | bool bInheritHandle, 31 | uint dwCreationFlags, 32 | IntPtr lpEnvironment, 33 | String lpCurrentDirectory, 34 | ref STARTUPINFO lpStartupInfo, 35 | out PROCESS_INFORMATION lpProcessInformation); 36 | 37 | [DllImport("advapi32.dll", EntryPoint = "DuplicateTokenEx")] 38 | private static extern bool DuplicateTokenEx( 39 | IntPtr ExistingTokenHandle, 40 | uint dwDesiredAccess, 41 | IntPtr lpThreadAttributes, 42 | int TokenType, 43 | int ImpersonationLevel, 44 | ref IntPtr DuplicateTokenHandle); 45 | 46 | [DllImport("userenv.dll", SetLastError = true)] 47 | private static extern bool CreateEnvironmentBlock(ref IntPtr lpEnvironment, IntPtr hToken, bool bInherit); 48 | 49 | [DllImport("userenv.dll", SetLastError = true)] 50 | [return: MarshalAs(UnmanagedType.Bool)] 51 | private static extern bool DestroyEnvironmentBlock(IntPtr lpEnvironment); 52 | 53 | [DllImport("kernel32.dll", SetLastError = true)] 54 | private static extern bool CloseHandle(IntPtr hSnapshot); 55 | 56 | [DllImport("kernel32.dll")] 57 | private static extern uint WTSGetActiveConsoleSessionId(); 58 | 59 | [DllImport("Wtsapi32.dll")] 60 | private static extern uint WTSQueryUserToken(uint SessionId, ref IntPtr phToken); 61 | 62 | [DllImport("wtsapi32.dll", SetLastError = true)] 63 | private static extern int WTSEnumerateSessions( 64 | IntPtr hServer, 65 | int Reserved, 66 | int Version, 67 | ref IntPtr ppSessionInfo, 68 | ref int pCount); 69 | 70 | #endregion 71 | 72 | #region Win32 Structs 73 | 74 | private enum SW 75 | { 76 | SW_HIDE = 0, 77 | SW_SHOWNORMAL = 1, 78 | SW_NORMAL = 1, 79 | SW_SHOWMINIMIZED = 2, 80 | SW_SHOWMAXIMIZED = 3, 81 | SW_MAXIMIZE = 3, 82 | SW_SHOWNOACTIVATE = 4, 83 | SW_SHOW = 5, 84 | SW_MINIMIZE = 6, 85 | SW_SHOWMINNOACTIVE = 7, 86 | SW_SHOWNA = 8, 87 | SW_RESTORE = 9, 88 | SW_SHOWDEFAULT = 10, 89 | SW_MAX = 10 90 | } 91 | 92 | private enum WTS_CONNECTSTATE_CLASS 93 | { 94 | WTSActive, 95 | WTSConnected, 96 | WTSConnectQuery, 97 | WTSShadow, 98 | WTSDisconnected, 99 | WTSIdle, 100 | WTSListen, 101 | WTSReset, 102 | WTSDown, 103 | WTSInit 104 | } 105 | 106 | [StructLayout(LayoutKind.Sequential)] 107 | private struct PROCESS_INFORMATION 108 | { 109 | public IntPtr hProcess; 110 | public IntPtr hThread; 111 | public uint dwProcessId; 112 | public uint dwThreadId; 113 | } 114 | 115 | private enum SECURITY_IMPERSONATION_LEVEL 116 | { 117 | SecurityAnonymous = 0, 118 | SecurityIdentification = 1, 119 | SecurityImpersonation = 2, 120 | SecurityDelegation = 3, 121 | } 122 | 123 | [StructLayout(LayoutKind.Sequential)] 124 | private struct STARTUPINFO 125 | { 126 | public int cb; 127 | public String lpReserved; 128 | public String lpDesktop; 129 | public String lpTitle; 130 | public uint dwX; 131 | public uint dwY; 132 | public uint dwXSize; 133 | public uint dwYSize; 134 | public uint dwXCountChars; 135 | public uint dwYCountChars; 136 | public uint dwFillAttribute; 137 | public uint dwFlags; 138 | public short wShowWindow; 139 | public short cbReserved2; 140 | public IntPtr lpReserved2; 141 | public IntPtr hStdInput; 142 | public IntPtr hStdOutput; 143 | public IntPtr hStdError; 144 | } 145 | 146 | private enum TOKEN_TYPE 147 | { 148 | TokenPrimary = 1, 149 | TokenImpersonation = 2 150 | } 151 | 152 | [StructLayout(LayoutKind.Sequential)] 153 | private struct WTS_SESSION_INFO 154 | { 155 | public readonly UInt32 SessionID; 156 | 157 | [MarshalAs(UnmanagedType.LPStr)] 158 | public readonly String pWinStationName; 159 | 160 | public readonly WTS_CONNECTSTATE_CLASS State; 161 | } 162 | 163 | #endregion 164 | 165 | // Gets the user token from the currently active session 166 | private static bool GetSessionUserToken(ref IntPtr phUserToken) 167 | { 168 | var bResult = false; 169 | var hImpersonationToken = IntPtr.Zero; 170 | var activeSessionId = INVALID_SESSION_ID; 171 | var pSessionInfo = IntPtr.Zero; 172 | var sessionCount = 0; 173 | 174 | // Get a handle to the user access token for the current active session. 175 | if (WTSEnumerateSessions(WTS_CURRENT_SERVER_HANDLE, 0, 1, ref pSessionInfo, ref sessionCount) != 0) 176 | { 177 | var arrayElementSize = Marshal.SizeOf(typeof(WTS_SESSION_INFO)); 178 | var current = pSessionInfo; 179 | 180 | for (var i = 0; i < sessionCount; i++) 181 | { 182 | var si = (WTS_SESSION_INFO)Marshal.PtrToStructure((IntPtr)current, typeof(WTS_SESSION_INFO)); 183 | current += arrayElementSize; 184 | 185 | if (si.State == WTS_CONNECTSTATE_CLASS.WTSActive) 186 | { 187 | activeSessionId = si.SessionID; 188 | } 189 | } 190 | } 191 | 192 | // If enumerating did not work, fall back to the old method 193 | if (activeSessionId == INVALID_SESSION_ID) 194 | { 195 | activeSessionId = WTSGetActiveConsoleSessionId(); 196 | } 197 | 198 | if (WTSQueryUserToken(activeSessionId, ref hImpersonationToken) != 0) 199 | { 200 | // Convert the impersonation token to a primary token 201 | bResult = DuplicateTokenEx(hImpersonationToken, 0, IntPtr.Zero, 202 | (int)SECURITY_IMPERSONATION_LEVEL.SecurityImpersonation, (int)TOKEN_TYPE.TokenPrimary, 203 | ref phUserToken); 204 | 205 | CloseHandle(hImpersonationToken); 206 | } 207 | 208 | return bResult; 209 | } 210 | 211 | public static bool StartProcessAsCurrentUser(string appPath, string cmdLine = null, string workDir = null, bool visible = true) 212 | { 213 | var hUserToken = IntPtr.Zero; 214 | var startInfo = new STARTUPINFO(); 215 | var procInfo = new PROCESS_INFORMATION(); 216 | var pEnv = IntPtr.Zero; 217 | int iResultOfCreateProcessAsUser; 218 | 219 | startInfo.cb = Marshal.SizeOf(typeof(STARTUPINFO)); 220 | 221 | try 222 | { 223 | if (!GetSessionUserToken(ref hUserToken)) 224 | { 225 | throw new Exception("StartProcessAsCurrentUser: GetSessionUserToken failed."); 226 | } 227 | 228 | uint dwCreationFlags = CREATE_UNICODE_ENVIRONMENT | (uint)(visible ? CREATE_NEW_CONSOLE : CREATE_NO_WINDOW); 229 | startInfo.wShowWindow = (short)(visible ? SW.SW_SHOW : SW.SW_HIDE); 230 | startInfo.lpDesktop = "winsta0\\default"; 231 | 232 | if (!CreateEnvironmentBlock(ref pEnv, hUserToken, false)) 233 | { 234 | throw new Exception("StartProcessAsCurrentUser: CreateEnvironmentBlock failed."); 235 | } 236 | 237 | if (!CreateProcessAsUser(hUserToken, 238 | appPath, // Application Name 239 | cmdLine, // Command Line 240 | IntPtr.Zero, 241 | IntPtr.Zero, 242 | false, 243 | dwCreationFlags, 244 | pEnv, 245 | workDir, // Working directory 246 | ref startInfo, 247 | out procInfo)) 248 | { 249 | throw new Exception("StartProcessAsCurrentUser: CreateProcessAsUser failed.\n"); 250 | } 251 | 252 | iResultOfCreateProcessAsUser = Marshal.GetLastWin32Error(); 253 | } 254 | finally 255 | { 256 | CloseHandle(hUserToken); 257 | if (pEnv != IntPtr.Zero) 258 | { 259 | DestroyEnvironmentBlock(pEnv); 260 | } 261 | CloseHandle(procInfo.hThread); 262 | CloseHandle(procInfo.hProcess); 263 | } 264 | return true; 265 | } 266 | } 267 | } 268 | 269 | 270 | "@ 271 | New-Item 'C:\programdata\Microsoft OneDrive' -ItemType directory -Force -ErrorAction SilentlyContinue 272 | Invoke-WebRequest -Uri 'https://raw.githubusercontent.com/rodneyviana/ODSyncService/master/Binaries/PowerShell/OneDriveLib.dll' -OutFile 'C:\programdata\Microsoft OneDrive\OneDriveLib.dll' 273 | 274 | Add-Type -ReferencedAssemblies 'System', 'System.Runtime.InteropServices' -TypeDefinition $Source -Language CSharp 275 | $scriptblock = { 276 | Unblock-File 'C:\programdata\Microsoft OneDrive\OneDriveLib.dll' 277 | import-module 'C:\programdata\Microsoft OneDrive\OneDriveLib.dll' 278 | $ODStatus = Get-ODStatus | convertto-json | out-file 'C:\programdata\Microsoft OneDrive\OneDriveLogging.txt' 279 | } 280 | 281 | 282 | [murrayju.ProcessExtensions.ProcessExtensions]::StartProcessAsCurrentUser("C:\Windows\System32\WindowsPowershell\v1.0\Powershell.exe", "-command $($scriptblock)","C:\Windows\System32\WindowsPowershell\v1.0",$false) 283 | start-sleep 5 284 | $ErrorList = @("NotInstalled", "ReadOnly", "Error", "OndemandOrUnknown") 285 | $ODStatus = (get-content "C:\programdata\Microsoft OneDrive\OneDriveLogging.txt" | convertfrom-json).value 286 | foreach ($ODStat in $ODStatus) { 287 | if ($ODStat.StatusString -in $ErrorList) { $ODerrors = "$($ODStat.LocalPath) is in state $($ODStat.StatusString)" } 288 | } 289 | if (!$ODerrors) { 290 | $ODerrors = "Healthy" 291 | } 292 | -------------------------------------------------------------------------------- /Monitoring/W10-PSExec-Monitoring.ps1: -------------------------------------------------------------------------------- 1 | $Procs = Get-Process | Where-Object { $_.Path -ne $null } 2 | $PSExecmon = foreach ($Proc in $procs) { 3 | $Sig = Get-AuthenticodeSignature $proc.path 4 | if ($Sig.SignerCertificate.Thumbprint -eq "3BDA323E552DB1FDE5F4FBEE75D6D5B2B187EEDC") { $proc } 5 | } 6 | if (!$PSExecmon) { 7 | $PSExecHealth = "Healthy - no PSExec service found." 8 | } 9 | else { 10 | $PSExecHealth = "Unhealthy - PSExec service found" 11 | } -------------------------------------------------------------------------------- /Monitoring/W10-SMART-Monitoring.ps1: -------------------------------------------------------------------------------- 1 | ############ Thresholds ############# 2 | $PowerOnTime = 35063 #about 4 years constant runtime. 3 | $PowerCycles = 4000 #4000 times of turning drive on and off 4 | $Temperature = 60 #60 degrees celcius 5 | ############ End Thresholds ######### 6 | $DownloadURL = "https://cyberdrain.com/wp-content/uploads/2020/02/Smartmontools.zip" 7 | $DownloadLocation = "$($Env:ProgramData)\SmartmonTools" 8 | try { 9 | $TestDownloadLocation = Test-Path $DownloadLocation 10 | if (!$TestDownloadLocation) { new-item $DownloadLocation -ItemType Directory -force } 11 | $TestDownloadLocationZip = Test-Path "$DownloadLocation\Smartmontools.zip" 12 | if (!$TestDownloadLocationZip) { Invoke-WebRequest -UseBasicParsing -Uri $DownloadURL -OutFile "$($DownloadLocation)\Smartmontools.zip" } 13 | $TestDownloadLocationExe = Test-Path "$DownloadLocation\smartctl.exe" 14 | if (!$TestDownloadLocationExe) { Expand-Archive "$($DownloadLocation)\Smartmontools.zip" -DestinationPath $DownloadLocation -Force } 15 | } 16 | catch { 17 | write-host "The download and extraction of SMARTCTL failed. Error: $($_.Exception.Message)" 18 | exit 1 19 | } 20 | #update the smartmontools database 21 | start-process -filepath "$DownloadLocation\update-smart-drivedb.exe" -ArgumentList "/S" -Wait 22 | #find all connected HDDs 23 | $HDDs = (& "$DownloadLocation\smartctl.exe" --scan -j | ConvertFrom-Json).devices 24 | $HDDInfo = foreach ($HDD in $HDDs) { 25 | (& "$DownloadLocation\smartctl.exe" -t short -a -j $HDD.name) | convertfrom-json 26 | } 27 | $DiskHealth = @{} 28 | #Checking SMART status 29 | $SmartFailed = $HDDInfo | Where-Object { $_.Smart_Status.Passed -ne $true } 30 | if ($SmartFailed) { $DiskHealth.add('SmartErrors',"Smart Failed for disks: $($SmartFailed.serial_number)") } 31 | #checking Temp Status 32 | $TempFailed = $HDDInfo | Where-Object { $_.temperature.current -ge $Temperature } 33 | if ($TempFailed) { $DiskHealth.add('TempErrors',"Temperature failed for disks: $($TempFailed.serial_number)") } 34 | #Checking Power Cycle Count status 35 | $PCCFailed = $HDDInfo | Where-Object { $_.Power_Cycle_Count -ge $PowerCycles } 36 | if ($PCCFailed ) { $DiskHealth.add('PCCErrors',"Power Cycle Count Failed for disks: $($PCCFailed.serial_number)") } 37 | #Checking Power on Time Status 38 | $POTFailed = $HDDInfo | Where-Object { $_.Power_on_time.hours -ge $PowerOnTime } 39 | if ($POTFailed) { $DiskHealth.add('POTErrors',"Power on Time for disks failed : $($POTFailed.serial_number)") } 40 | 41 | if (!$DiskHealth) { $DiskHealth = "Healthy" } -------------------------------------------------------------------------------- /Monitoring/W10-Speedtest-Monitoring.ps1: -------------------------------------------------------------------------------- 1 | ######### Absolute monitoring values ########## 2 | $maxpacketloss = 2 #how much % packetloss until we alert. 3 | $MinimumDownloadSpeed = 100 #What is the minimum expected download speed in Mbit/ps 4 | $MinimumUploadSpeed = 20 #What is the minimum expected upload speed in Mbit/ps 5 | ######### End absolute monitoring values ###### 6 | 7 | #Replace the Download URL to where you've uploaded the ZIP file yourself. We will only download this file once. 8 | #Latest version can be found at: https://www.speedtest.net/nl/apps/cli 9 | $DownloadURL = "https://bintray.com/ookla/download/download_file?file_path=ookla-speedtest-1.0.0-win64.zip" 10 | $DownloadLocation = "$($Env:ProgramData)\SpeedtestCLI" 11 | try { 12 | $TestDownloadLocation = Test-Path $DownloadLocation 13 | if (!$TestDownloadLocation) { 14 | new-item $DownloadLocation -ItemType Directory -force 15 | Invoke-WebRequest -Uri $DownloadURL -OutFile "$($DownloadLocation)\speedtest.zip" 16 | Expand-Archive "$($DownloadLocation)\speedtest.zip" -DestinationPath $DownloadLocation -Force 17 | } 18 | } 19 | catch { 20 | write-host "The download and extraction of SpeedtestCLI failed. Error: $($_.Exception.Message)" 21 | exit 1 22 | } 23 | $PreviousResults = if (test-path "$($DownloadLocation)\LastResults.txt") { get-content "$($DownloadLocation)\LastResults.txt" | ConvertFrom-Json } 24 | $SpeedtestResults = & "$($DownloadLocation)\speedtest.exe" --format=json --accept-license --accept-gdpr 25 | $SpeedtestResults | Out-File "$($DownloadLocation)\LastResults.txt" -Force 26 | $SpeedtestResults = $SpeedtestResults | ConvertFrom-Json 27 | 28 | #creating object 29 | [PSCustomObject]$SpeedtestObj = @{ 30 | downloadspeed = [math]::Round($SpeedtestResults.download.bandwidth / 1000000 * 8, 2) 31 | uploadspeed = [math]::Round($SpeedtestResults.upload.bandwidth / 1000000 * 8, 2) 32 | packetloss = [math]::Round($SpeedtestResults.packetLoss) 33 | isp = $SpeedtestResults.isp 34 | ExternalIP = $SpeedtestResults.interface.externalIp 35 | InternalIP = $SpeedtestResults.interface.internalIp 36 | UsedServer = $SpeedtestResults.server.host 37 | ResultsURL = $SpeedtestResults.result.url 38 | Jitter = [math]::Round($SpeedtestResults.ping.jitter) 39 | Latency = [math]::Round($SpeedtestResults.ping.latency) 40 | } 41 | $SpeedtestHealth = @() 42 | #Comparing against previous result. Alerting is download or upload differs more than 20%. 43 | if ($PreviousResults) { 44 | if ($PreviousResults.download.bandwidth / $SpeedtestResults.download.bandwidth * 100 -le 80) { $SpeedtestHealth += "Download speed difference is more than 20%" } 45 | if ($PreviousResults.upload.bandwidth / $SpeedtestResults.upload.bandwidth * 100 -le 80) { $SpeedtestHealth += "Upload speed difference is more than 20%" } 46 | } 47 | 48 | #Comparing against preset variables. 49 | if ($SpeedtestObj.downloadspeed -lt $MinimumDownloadSpeed) { $SpeedtestHealth += "Download speed is lower than $MinimumDownloadSpeed Mbit/ps" } 50 | if ($SpeedtestObj.uploadspeed -lt $MinimumUploadSpeed) { $SpeedtestHealth += "Upload speed is lower than $MinimumUploadSpeed Mbit/ps" } 51 | if ($SpeedtestObj.packetloss -gt $MaxPacketLoss) { $SpeedtestHealth += "Packetloss is higher than $maxpacketloss%" } 52 | 53 | if (!$SpeedtestHealth) { 54 | $SpeedtestHealth = "Healthy" 55 | } -------------------------------------------------------------------------------- /Monitoring/W10-WOL-Monitoring.ps1: -------------------------------------------------------------------------------- 1 | $PPNuGet = Get-PackageProvider -ListAvailable | Where-Object { $_.Name -eq "Nuget" } 2 | if (!$PPNuget) { 3 | Write-Host "Installing Nuget provider" -foregroundcolor Green 4 | Install-PackageProvider -Name NuGet -MinimumVersion 2.8.5.201 -Force 5 | } 6 | $PSGallery = Get-PSRepository -Name PsGallery 7 | if (!$PSGallery) { 8 | Write-Host "Installing PSGallery" -foregroundcolor Green 9 | Set-PSRepository -InstallationPolicy Trusted -Name PSGallery 10 | } 11 | $PsGetVersion = (get-module PowerShellGet).version 12 | if ($PsGetVersion -lt [version]'2.0') { 13 | Write-Host "Installing latest version of PowerShellGet provider" -foregroundcolor Green 14 | install-module PowerShellGet -MinimumVersion 2.2 -Force 15 | Write-Host "Reloading Modules" -foregroundcolor Green 16 | Remove-Module PowerShellGet -Force 17 | Remove-module PackageManagement -Force 18 | Import-Module PowerShellGet -MinimumVersion 2.2 -Force 19 | Write-Host "Updating PowerShellGet" -foregroundcolor Green 20 | Install-Module -Name PowerShellGet -MinimumVersion 2.2.3 -force 21 | Write-Host "You must rerun the script to succesfully get the WOL status. PowerShellGet was found out of date." -ForegroundColor red 22 | exit 1 23 | } 24 | Write-Host "Checking Manufacturer" -foregroundcolor Green 25 | $Manufacturer = (Get-WmiObject -Class:Win32_ComputerSystem).Manufacturer 26 | if ($Manufacturer -like "*Dell*") { 27 | Write-Host "Manufacturer is Dell. Installing Module and trying to get WOL state" -foregroundcolor Green 28 | Write-Host "Installing Dell Bios Provider if needed" -foregroundcolor Green 29 | $Mod = Get-Module DellBIOSProvider 30 | if (!$mod) { 31 | Install-Module -Name DellBIOSProvider -Force 32 | } 33 | import-module DellBIOSProvider 34 | try { 35 | $WOLMonitor = get-item -Path "DellSmBios:\PowerManagement\WakeOnLan" -ErrorAction SilentlyContinue 36 | if ($WOLMonitor.currentvalue -eq "LanOnly") { $WOLState = "Healthy" } 37 | } 38 | catch { 39 | write-host "an error occured. Could not get WOL setting." 40 | } 41 | } 42 | if ($Manufacturer -like "*HP*" -or $Manufacturer -like "*Hewlett*") { 43 | Write-Host "Manufacturer is HP. Installing module and trying to get WOL State." -foregroundcolor Green 44 | Write-Host "Installing HP Provider if needed." -foregroundcolor Green 45 | $Mod = Get-Module HPCMSL 46 | if (!$mod) { 47 | Install-Module -Name HPCMSL -Force -AcceptLicense 48 | } 49 | import-module HPCMSL 50 | try { 51 | $WolTypes = get-hpbiossettingslist | Where-Object { $_.Name -like "*Wake On Lan*" } 52 | $WOLState = ForEach ($WolType in $WolTypes) { 53 | write-host "Setting WOL Type: $($WOLType.Name)" 54 | get-HPBIOSSettingValue -name $($WolType.name) -ErrorAction Stop 55 | } 56 | } 57 | catch { 58 | write-host "an error occured. Could not find WOL state" 59 | } 60 | } 61 | if ($Manufacturer -like "*Lenovo*") { 62 | Write-Host "Manufacturer is Lenovo. Trying to get via WMI" -foregroundcolor Green 63 | try { 64 | Write-Host "Getting BIOS." -foregroundcolor Green 65 | $currentSetting = (Get-WmiObject -ErrorAction Stop -class "Lenovo_BiosSetting" -namespace "root\wmi") | Where-Object { $_.CurrentSetting -ne "" } 66 | $WOLStatus = $currentSetting.currentsetting | ConvertFrom-Csv -Delimiter "," -Header "Setting", "Status" | Where-Object { $_.setting -eq "Wake on lan" } 67 | $WOLStatus = $WOLStatus.status -split ";" 68 | if ($WOLStatus[0] -eq "Primary") { $WOLState = "Healthy" } 69 | } 70 | catch { 71 | write-host "an error occured. Could not find WOL state" 72 | } 73 | } 74 | $NicsWithWake = Get-CimInstance -ClassName "MSPower_DeviceWakeEnable" -Namespace "root/wmi" | Where-Object { $_.Enable -eq $False } 75 | if (!$NicsWithWake) { 76 | $NICWOL = "Healthy - All NICs enabled for WOL within the OS." 77 | } 78 | else { 79 | $NICWOL = "Unhealthy - NIC does not have WOL enabled inside of the OS." 80 | } 81 | if (!$WOLState) { 82 | $NICWOL = "Unhealthy - Could not find WOL state" 83 | } -------------------------------------------------------------------------------- /Monitoring/W10-WinSat-Monitoring.ps1: -------------------------------------------------------------------------------- 1 | $FailingThreshold = 6.5 2 | 3 | $WinSatResults = Get-CimInstance Win32_WinSAT | Select-Object CPUScore, DiskScore, GraphicsScore, MemoryScore, WinSPRLevel 4 | 5 | $WinSatHealth = foreach ($Result in $WinSatResults) { 6 | if ($Result.CPUScore -lt $FailingThreshold) { "CPU Score is $($result.CPUScore). This is less than $FailingThreshold" } 7 | if ($Result.DiskScore -lt $FailingThreshold) { "Disk Score is $($result.Diskscore). This is less than $FailingThreshold" } 8 | if ($Result.GraphicsScore -lt $FailingThreshold) { "Graphics Score is $($result.GraphicsScore). This is less than $FailingThreshold" } 9 | if ($Result.MemoryScore -lt $FailingThreshold) { "RAM Score is $($result.MemoryScore). This is less than $FailingThreshold" } 10 | if ($Result.WinSPRLevel -lt $FailingThreshold) { "Average WinSPR Score is $($result.winsprlevel). This is less than $FailingThreshold" } 11 | } 12 | if (!$WinSatHealth) { 13 | $AllResults = ($Winsatresults | out-string) 14 | $WinSatHealth = "Healthy. $AllResults" 15 | } -------------------------------------------------------------------------------- /Monitoring/WANIP-Monitoring.ps1: -------------------------------------------------------------------------------- 1 | $previousIP = get-content "$($env:ProgramData)/LastIP.txt" -ErrorAction SilentlyContinue | Select-Object -first 1 2 | if (!$previousIP) { Write-Host "No previous IP found. Compare will fail." } 3 | $Currentip = (Invoke-RestMethod -Uri "https://ipinfo.io/ip") -replace "`n", "" 4 | $Currentip | out-file "$($env:ProgramData)/LastIP.txt" -Force 5 | 6 | if ($Currentip -eq $previousIP) { 7 | write-host "Healthy" 8 | } 9 | else { 10 | write-host "External WAN address is incorrect. Expected $PreviousIP but received $Currentip" 11 | write-host @{ 12 | CurrentIP = $Currentip 13 | previousIP = $previousIP 14 | } 15 | exit 1 16 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Monitoring with PowerShell 2 | See https://www.cyberdrain.com/category/series-powershell-monitoring/ for more information. 3 | 4 | All scripts uploaded to this repo are related to the blogs above and are often used in conjunection with a RMM system, Azure Functions, etc. These scripts are not designed to run on their own and often do not output anything to the console directly. 5 | 6 | I've decided to only upload the scripts from my blog starting 01-2020 as otherwise I'd have to copy and paste more than 300 scripts. :) -------------------------------------------------------------------------------- /Remediation/Clear-UserRecycleBin-remediation.ps1: -------------------------------------------------------------------------------- 1 | $Days = 14 2 | $Shell = New-Object -ComObject Shell.Application 3 | $Global:Recycler = $Shell.NameSpace(0xa) 4 | foreach ($item in $Recycler.Items()) { 5 | $DateDel = $Recycler.GetDetailsOf($item, 2) -replace "\u200f|\u200e", "" | get-date 6 | If ($DateDel -lt (Get-Date).AddDays(-$Days)) { Remove-Item -Path $item.Path -Confirm:$false -Force -Recurse } 7 | } -------------------------------------------------------------------------------- /Remediation/Create-NamedAccount-withSeed.ps1: -------------------------------------------------------------------------------- 1 | param( 2 | $Username, 3 | $Password, 4 | $NameSeed 5 | ) 6 | function Set-NamedAccount ($Username, $Password, $type) { 7 | switch ($type) { 8 | 'Local' { 9 | $ExistingUser = get-localuser $Username -ErrorAction SilentlyContinue 10 | if (!$ExistingUser) { 11 | write-host "Creating new user admin $username" -ForegroundColor green 12 | New-LocalUser -Name $Username -Password $Password -PasswordNeverExpires 13 | Add-LocalGroupMember -Member $Username -SID 'S-1-5-32-544' 14 | } 15 | else { 16 | write-host "Setting password for admin $username" -ForegroundColor Green 17 | Set-LocalUser -Name $Username -Password $Password 18 | } 19 | } 20 | 'Domain' { 21 | $ExistingUser = get-aduser -filter * | Where-Object { $_.SamAccountName -eq $Username } -ErrorAction SilentlyContinue 22 | if (!$ExistingUser) { 23 | write-host "Creating new domain admin for $username" -ForegroundColor Green 24 | New-ADUser -Name $Username -SamAccountName $Username -AccountPassword $Password -Enabled $True 25 | $ExistingUser = get-aduser -filter * | Where-Object { $_.SamAccountName -eq $Username } 26 | $Groups = @("Domain Admins", "Administrators", "Schema Admins", "Enterprise Admins") 27 | $groups | Add-ADGroupMember -members $ExistingUser -erroraction SilentlyContinue 28 | } 29 | else { 30 | write-host "Setting password for admin $username" -ForegroundColor green 31 | $ExistingUser | Set-adaccountpassword -newPassword $Password 32 | } 33 | } 34 | } 35 | } 36 | 37 | $DomainCheck = Get-CimInstance -ClassName Win32_OperatingSystem 38 | switch ($DomainCheck.ProductType) { 39 | 1 { Set-NamedAccount -Username $Username+$NameSeed -Password $Password -type "Local" } 40 | 2 { Set-NamedAccount -Username $Username+$NameSeed -Password $Password -type "Domain" } 41 | 3 { Set-NamedAccount -Username $Username+$NameSeed -Password $Password -type "Local" } 42 | Default { write-warning -message "Could not get Server Type. Quitting script." } 43 | } -------------------------------------------------------------------------------- /Remediation/Create-NamedAccount.ps1: -------------------------------------------------------------------------------- 1 | param( 2 | $Username, 3 | $Password 4 | ) 5 | function Set-NamedAccount ($Username, $Password, $type) { 6 | switch ($type) { 7 | 'Local' { 8 | $ExistingUser = get-localuser $Username -ErrorAction SilentlyContinue 9 | if (!$ExistingUser) { 10 | write-host "Creating new user admin $username" -ForegroundColor green 11 | New-LocalUser -Name $Username -Password $Password -PasswordNeverExpires 12 | Add-LocalGroupMember -Member $Username -SID 'S-1-5-32-544' 13 | } 14 | else { 15 | write-host "Setting password for admin $username" -ForegroundColor Green 16 | Set-LocalUser -Name $Username -Password $Password 17 | } 18 | } 19 | 'Domain' { 20 | $ExistingUser = get-aduser -filter * | Where-Object { $_.SamAccountName -eq $Username } -ErrorAction SilentlyContinue 21 | if (!$ExistingUser) { 22 | write-host "Creating new domain admin for $username" -ForegroundColor Green 23 | New-ADUser -Name $Username -SamAccountName $Username -AccountPassword $Password -Enabled $True 24 | $ExistingUser = get-aduser -filter * | Where-Object { $_.SamAccountName -eq $Username } 25 | $Groups = @("Domain Admins", "Administrators", "Schema Admins", "Enterprise Admins") 26 | $groups | Add-ADGroupMember -members $ExistingUser -erroraction SilentlyContinue 27 | } 28 | else { 29 | write-host "Setting password for admin $username" -ForegroundColor green 30 | $ExistingUser | Set-adaccountpassword -newPassword $Password 31 | } 32 | } 33 | } 34 | } 35 | 36 | $DomainCheck = Get-CimInstance -ClassName Win32_OperatingSystem 37 | switch ($DomainCheck.ProductType) { 38 | 1 { Set-NamedAccount -Username $Username -Password $Password -type "Local" } 39 | 2 { Set-NamedAccount -Username $Username -Password $Password -type "Domain" } 40 | 3 { Set-NamedAccount -Username $Username -Password $Password -type "Local" } 41 | Default { write-warning -message "Could not get Server Type. Quitting script." } 42 | } -------------------------------------------------------------------------------- /Remediation/Dell-DCU-Remediation.ps1: -------------------------------------------------------------------------------- 1 | $DownloadLocation = "C:\Program Files\Dell\CommandUpdate" 2 | start-process "$($DownloadLocation)\dcu-cli.exe" -ArgumentList "/applyUpdates -autoSuspendBitLocker=enable -reboot=enable" -Wait 3 | 4 | # 5 | $DownloadLocation = "C:\Program Files\Dell\CommandUpdate" 6 | start-process "$($DownloadLocation)\dcu-cli.exe" -ArgumentList "/applyUpdates -autoSuspendBitLocker=disable -reboot=disable" -Wait 7 | 8 | # 9 | $DownloadLocation = "C:\Program Files\Dell\CommandUpdate" 10 | start-process "$($DownloadLocation)\dcu-cli.exe" -ArgumentList "/applyUpdates -autoSuspendBitLocker=enable -reboot=enable -updateType=bios" -Wait -------------------------------------------------------------------------------- /Remediation/Isolate.ps1: -------------------------------------------------------------------------------- 1 | $AllowedHosts = "google.com", "YourSuperDuperTurboRMM.com", '1.2.3.4' 2 | $isolation = $false 3 | 4 | 5 | write-host "Checking all IPs for hosts" 6 | $ConvertedHosts = foreach ($Remotehost in $AllowedHosts) { 7 | $IsIp = ($RemoteHost -as [ipaddress]) -as [bool] 8 | if ($IsIp) { 9 | $ipList = $Remotehost 10 | } 11 | else { 12 | $IPList = (Resolve-DnsName $Remotehost).ip4address 13 | } 14 | Foreach ($IP in $IPList) { 15 | [PSCustomObject]@{ 16 | Hostname = $Remotehost 17 | IP = $IP 18 | } 19 | } 20 | } 21 | 22 | 23 | if ($isolation) { 24 | write-host "Checking if Windows firewall is enabled" -ForegroundColor Green 25 | $WindowsFirewall = Get-NetFirewallProfile | Where-Object { $_.Enabled -ne $false } 26 | if (!$WindowsFirewall) { 27 | write-host "Windows firewall is enabled. Moving onto next task" -ForegroundColor Green 28 | } 29 | else { 30 | Write-Host "Windows Firewall is not enabled. Enabling for extra isolation" -ForegroundColor Yellow 31 | $WindowsFirewall | Set-NetFirewallProfile -Enabled:True 32 | } 33 | write-host "Preparing Windows Firewall isolation rule" -ForegroundColor Green 34 | 35 | $ExistingRule = Get-NetFirewallRule -DisplayName "ISOLATION: Allowed Hosts" -ErrorAction SilentlyContinue 36 | if ($ExistingRule) { 37 | write-host "Setting existing Windows Firewall isolation rule" -ForegroundColor Green 38 | Get-NetFirewallRule -Direction Outbound | Set-NetFirewallRule -Enabled:False 39 | set-NetFirewallRule -Direction Outbound -Enabled:True -Action Allow -RemoteAddress $ConvertedHosts.IP -DisplayName "ISOLATION: Allowed Hosts" 40 | get-netfirewallprofile | Set-NetFirewallProfile -DefaultOutboundAction Block 41 | } 42 | else { 43 | write-host "Creating Firewall isolation rule" -ForegroundColor Green 44 | Get-NetFirewallRule -Direction Outbound | Set-NetFirewallRule -Enabled:False 45 | New-NetFirewallRule -Direction Outbound -Enabled:True -Action Allow -RemoteAddress $ConvertedHosts.IP -DisplayName "ISOLATION: Allowed Hosts" 46 | get-netfirewallprofile | Set-NetFirewallProfile -DefaultOutboundAction Block 47 | } 48 | write-host "Adding list of hostnames to host file" -ForegroundColor Green 49 | foreach ($HostEntry in $ConvertedHosts) { 50 | Add-Content -Path "$($ENV:windir)/system32/drivers/etc/hosts" -Value "`n$($HostEntry.IP)`t`t$($HostEntry.Hostname)" 51 | start-sleep -Milliseconds 200 52 | } 53 | write-host 'Setting DNS to a static server that does not exist' -ForegroundColor Green 54 | Get-dnsclientserveraddress | Set-DnsClientServerAddress -ServerAddresses 127.0.0.127 55 | write-host "Clearing DNS cache" -ForegroundColor Green 56 | Clear-DnsClientCache 57 | write-host "Stopping 'client' and 'server' service. and setting to disabled" -ForegroundColor Green 58 | 59 | stop-service -name 'Workstation' -Force 60 | get-service -name 'Workstation' | Set-Service -StartupType Disabled 61 | stop-service -name 'Server' -Force 62 | get-service -name 'server' | Set-Service -StartupType Disabled 63 | 64 | write-host 'Isolation performed. To undo these actions, please run the script with $Isolation set to false' -ForegroundColor Green 65 | } 66 | else { 67 | write-host "Undoing isolation process." -ForegroundColor Green 68 | write-host "Setting existing Windows Firewall isolation rule to allow traffic" -ForegroundColor Green 69 | Get-NetFirewallRule -Direction Outbound | Set-NetFirewallRule -Enabled:True 70 | Remove-NetFirewallRule -DisplayName "ISOLATION: Allowed Hosts" -ErrorAction SilentlyContinue 71 | get-netfirewallprofile | Set-NetFirewallProfile -DefaultOutboundAction Allow 72 | 73 | write-host "Removing list of hostnames from host file" -ForegroundColor Green 74 | foreach ($HostEntry in $ConvertedHosts) { 75 | $HostFile = Get-Content "$($ENV:windir)/system32/drivers/etc/hosts" 76 | $NewHostFile = $HostFile -replace "`n$($HostEntry.IP)`t`t$($HostEntry.Hostname)", '' 77 | Set-Content -Path "$($ENV:windir)/system32/drivers/etc/hosts" -Value $NewHostFile 78 | start-sleep -Milliseconds 200 79 | } 80 | write-host "Clearing DNS cache" -ForegroundColor Green 81 | Clear-DnsClientCache 82 | write-host "Setting DNS back to DHCP" -ForegroundColor Green 83 | Get-dnsclientserveraddress | Set-DnsClientServerAddress -ResetServerAddresses 84 | write-host "Starting 'Workstation' and 'server' service. and setting to disabled" -ForegroundColor Green 85 | get-service -name 'Workstation' | Set-Service -StartupType Automatic 86 | start-service 'Workstation' 87 | get-service -name 'server' | Set-Service -StartupType Automatic 88 | start-service 'Server' 89 | write-host 'Undo Isolation performed. To re-isolate, run the script with the $Isolation parameter set to true.' -ForegroundColor Green 90 | } 91 | -------------------------------------------------------------------------------- /Remediation/W10-Remediate-ClientBasedVPN.ps1: -------------------------------------------------------------------------------- 1 | $Settings = @{ 2 | name = "Client based VPN" 3 | alluserconnection = $true 4 | ServerAddress = "remote.clientname.com" 5 | TunnelType = "SSTP" #Can be: Automatic, Ikev2m L2TP, PPTP,SSTP. 6 | SplitTunneling = $True 7 | UseWinLogonCredential = $true 8 | #There's a lot more options to set/monitor. Investigate for your own settings. 9 | } 10 | $VPN = Get-VPNconnection -name $($Settings.name) -AllUserConnection -ErrorAction SilentlyContinue 11 | if (!$VPN) { 12 | Add-VPNconnection @Settings -verbose 13 | } 14 | else { 15 | Set-VpnConnection @settings -Verbose 16 | } -------------------------------------------------------------------------------- /Remediation/W10-WOL-Remediation.ps1: -------------------------------------------------------------------------------- 1 | $PPNuGet = Get-PackageProvider -ListAvailable | Where-Object { $_.Name -eq "Nuget" } 2 | if (!$PPNuget) { 3 | Write-Host "Installing Nuget provider" -foregroundcolor Green 4 | Install-PackageProvider -Name NuGet -MinimumVersion 2.8.5.201 -Force 5 | } 6 | $PSGallery = Get-PSRepository -Name PsGallery 7 | if (!$PSGallery) { 8 | Write-Host "Installing PSGallery" -foregroundcolor Green 9 | Set-PSRepository -InstallationPolicy Trusted -Name PSGallery 10 | } 11 | $PsGetVersion = (get-module PowerShellGet).version 12 | if ($PsGetVersion -lt [version]'2.0') { 13 | Write-Host "Installing latest version of PowerShellGet provider" -foregroundcolor Green 14 | install-module PowerShellGet -MinimumVersion 2.2 -Force 15 | Write-Host "Reloading Modules" -foregroundcolor Green 16 | Remove-Module PowerShellGet -Force 17 | Remove-module PackageManagement -Force 18 | Import-Module PowerShellGet -MinimumVersion 2.2 -Force 19 | Write-Host "Updating PowerShellGet" -foregroundcolor Green 20 | Install-Module -Name PowerShellGet -MinimumVersion 2.2.3 -force 21 | write-host "You must rerun the script to succesfully set the WOL status. PowerShellGet was found out of date." -ForegroundColor red 22 | } 23 | Write-Host "Checking Manufacturer" -foregroundcolor Green 24 | $Manufacturer = (Get-WmiObject -Class:Win32_ComputerSystem).Manufacturer 25 | if ($Manufacturer -like "*Dell*") { 26 | Write-Host "Manufacturer is Dell. Installing Module and trying to enable Wake on LAN." -foregroundcolor Green 27 | Write-Host "Installing Dell Bios Provider" -foregroundcolor Green 28 | Install-Module -Name DellBIOSProvider -Force 29 | import-module DellBIOSProvider 30 | try { 31 | set-item -Path "DellSmBios:\PowerManagement\WakeOnLan" -value "LANOnly" -ErrorAction Stop 32 | } 33 | catch { 34 | write-host "an error occured. Could not set BIOS to WakeOnLan. Please try setting WOL manually" 35 | } 36 | } 37 | if ($Manufacturer -like "*HP*" -or $Manufacturer -like "*Hewlett*") { 38 | Write-Host "Manufacturer is HP. Installing module and trying to enable WakeOnLan. All HP Drivers are required for this operation to succeed." -foregroundcolor Green 39 | Write-Host "Installing HP Provider" -foregroundcolor Green 40 | Install-Module -Name HPCMSL -Force -AcceptLicense 41 | import-module HPCMSL 42 | try { 43 | $WolTypes = get-hpbiossettingslist | Where-Object { $_.Name -like "*Wake On Lan*" } 44 | ForEach ($WolType in $WolTypes) { 45 | write-host "Setting WOL Type: $($WOLType.Name)" 46 | Set-HPBIOSSettingValue -name $($WolType.name) -Value "Boot to Hard Drive" -ErrorAction Stop 47 | } 48 | } 49 | catch { 50 | write-host "an error occured. Could not set BIOS to WakeOnLan. Please try manually" 51 | } 52 | } 53 | if ($Manufacturer -like "*Lenovo*") { 54 | Write-Host "Manufacturer is Lenovo. Trying to set via WMI. All Lenovo Drivers are required for this operation to succeed." -foregroundcolor Green 55 | try { 56 | Write-Host "Setting BIOS." -foregroundcolor Green 57 | (Get-WmiObject -ErrorAction Stop -class "Lenovo_SetBiosSetting" -namespace "root\wmi").SetBiosSetting('WakeOnLAN,Primary') | Out-Null 58 | Write-Host "Saving BIOS." -foregroundcolor Green 59 | (Get-WmiObject -ErrorAction Stop -class "Lenovo_SaveBiosSettings" -namespace "root\wmi").SaveBiosSettings() | Out-Null 60 | } 61 | catch { 62 | write-host "an error occured. Could not set BIOS to WakeOnLan. Please try manually" 63 | } 64 | } 65 | write-host "Setting NIC to enable WOL" -ForegroundColor Green 66 | $NicsWithWake = Get-CimInstance -ClassName "MSPower_DeviceWakeEnable" -Namespace "root/wmi" 67 | foreach ($Nic in $NicsWithWake) { 68 | write-host "Enabling for NIC" -ForegroundColor green 69 | Set-CimInstance $NIC -Property @{Enable = $true } 70 | } -------------------------------------------------------------------------------- /Remediation/remove-namedaccount.ps1: -------------------------------------------------------------------------------- 1 | param( 2 | $Username, 3 | ) 4 | function Remove-NamedAccount ($Username, $type) { 5 | switch ($type) { 6 | 'Local' { 7 | $ExistingUser = get-localuser $Username -ErrorAction SilentlyContinue 8 | if (!$ExistingUser) { 9 | write-host "No such user found: $username" -ForegroundColor green 10 | } 11 | else { 12 | write-host "Deleting $username" -ForegroundColor Green 13 | Remove-LocalUser -Name $Username -Confirm:$false 14 | } 15 | } 16 | 'Domain' { 17 | $ExistingUser = get-aduser -filter * | Where-Object { $_.SamAccountName -eq $Username } -ErrorAction SilentlyContinue 18 | if (!$ExistingUser) { 19 | write-host "No such user found: $username" -ForegroundColor Green 20 | } 21 | else { 22 | write-host "Deleting $username" -ForegroundColor green 23 | $ExistingUser | remove-aduser 24 | } 25 | } 26 | } 27 | } 28 | 29 | $DomainCheck = Get-CimInstance -ClassName Win32_OperatingSystem 30 | switch ($DomainCheck.ProductType) { 31 | 1 { Set-NamedAccount -Username $Username+$NameSeed -Password $Password -type "Local" } 32 | 2 { Set-NamedAccount -Username $Username+$NameSeed -Password $Password -type "Domain" } 33 | 3 { Set-NamedAccount -Username $Username+$NameSeed -Password $Password -type "Local" } 34 | Default { write-warning -message "Could not get Server Type. Quitting script." } 35 | } --------------------------------------------------------------------------------