diff --git a/.vscode/launch.json b/.vscode/launch.json
new file mode 100644
index 0000000..19505d3
--- /dev/null
+++ b/.vscode/launch.json
@@ -0,0 +1,26 @@
+{
+ "version": "0.2.0",
+ "configurations": [
+ {
+ "type": "PowerShell",
+ "request": "launch",
+ "name": "PowerShell Launch (current file)",
+ "script": "${file}",
+ "args": [],
+ "cwd": "${file}"
+ },
+ {
+ "type": "PowerShell",
+ "request": "attach",
+ "name": "PowerShell Attach to Host Process",
+ "processId": "${command.PickPSHostProcess}",
+ "runspaceId": 1
+ },
+ {
+ "type": "PowerShell",
+ "request": "launch",
+ "name": "PowerShell Interactive Session",
+ "cwd": "${workspaceRoot}"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/AD/Get-ADCommonGroup.ps1 b/AD/Get-ADCommonGroup.ps1
new file mode 100644
index 0000000..cb2d4cd
--- /dev/null
+++ b/AD/Get-ADCommonGroup.ps1
@@ -0,0 +1,40 @@
+[CmdletBinding()]
+param (
+ [Parameter(Mandatory = $true)]
+ [ValidateScript({ Get-ADGroup -Identity $_ })]
+ [string]$GroupName
+)
+
+begin {
+ Import-Module ActiveDirectory
+ $Group = Get-ADGroup -Identity $GroupName
+ $AllGroups = @{}
+}
+
+process {
+ $Members = Get-ADGroupMember -Identity $Group
+
+ $Members | ForEach-Object {
+ $User = $_.SamAccountName
+
+ (Get-ADUser $User -Properties MemberOf).MemberOf | ForEach-Object {
+ # Check if the group exists in our list
+ if ($AllGroups.ContainsKey($_)) {
+ $AllGroups.($_)++
+ } else {
+ # Item is unique so add it to the list
+ $AllGroups.Add($_, 1)
+ }
+ }
+ }
+
+ Write-Host "Results:"
+ $AllGroups | Format-Table -AutoSize
+ $AllGroups.GetEnumerator() | Select-Object -Property Key, Value |
+ Export-Csv -Path .\$GroupName-CommonGroups.csv -NoTypeInformation
+
+ Write-Host "Common memberships:"
+ $AllGroups.GetEnumerator() | ForEach-Object {
+ if ($_.Value -eq $Members.Count) { $_.Key }
+ }
+}
\ No newline at end of file
diff --git a/AD/Get-ADGroupReport.ps1 b/AD/Get-ADGroupReport.ps1
new file mode 100644
index 0000000..a8fe501
--- /dev/null
+++ b/AD/Get-ADGroupReport.ps1
@@ -0,0 +1,119 @@
+function Get-ADGroupReport {
+ [CmdletBinding()]
+ Param(
+ [Parameter(Mandatory = $true)]
+ [ValidateNotNullorEmpty()]
+ [string]$SaveToCsv,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullorEmpty()]
+ [string]$ADGroupFilter = 'GroupCategory -eq "Distribution"'
+ )
+
+ begin {
+ Import-Module ActiveDirectory
+
+ Write-Progress -Id 1 -Activity "Getting AD groups using filter $ADGroupFilter..."
+ $Groups = Get-ADGroup -Filter $ADGroupFilter
+ $Users = @{}
+ }
+
+ process {
+ $GroupCount = $Groups.Count
+ $GroupCounter = 0
+
+ foreach ($Group in $Groups) {
+ $GroupCounter++
+
+ $GroupName = $Group.Name
+
+ $GroupProgressParams = @{
+ 'Id' = 1
+ 'Activity' = "Processing group $GroupCounter of $GroupCount"
+ 'Status' = $GroupName
+ 'PercentComplete' = ($GroupCounter / $GroupCount) * 100
+ }
+ Write-Progress @GroupProgressParams
+
+ $Members = Get-ADGroupMember $Group -Recursive
+
+ $MemberCount = $Members.Count
+ $MemberCounter = 0
+
+ foreach ($Member in $Members) {
+ $MemberCounter++
+
+ $MemberName = $Member.SamAccountName
+
+ $MemberProgressParams = @{
+ 'Id' = 2
+ 'ParentId' = 1
+ 'Activity' = "Processing member $MemberCounter of $MemberCount"
+ 'Status' = $MemberName
+ }
+ if ($MemberCount.GetType() -eq [int]) {
+ $MemberProgressParams.Add('PercentComplete', ($MemberCounter / $MemberCount) * 100)
+ }
+ Write-Progress @MemberProgressParams
+
+ if ($Member.objectClass -eq 'user') {
+ Write-Verbose "$MemberName is a user"
+
+ if (-not $Users.ContainsKey($MemberName)) {
+ Write-Verbose "Adding $MemberName to users list"
+ $Users.Add($MemberName, @{})
+ }
+
+ Write-Verbose "Adding $($GroupName) to $MemberName's group list"
+ $Users.($MemberName).Add($GroupName, $true)
+ } else {
+ Write-Verbose "$MemberName is not a user"
+ }
+ }
+
+ Write-Progress -Id 2 -ParentId 1 -Activity "Processing members" -Completed
+ }
+
+ $ReportProgressParams = @{
+ 'Id' = 1
+ 'Activity' = 'Building report...'
+ }
+ Write-Progress @ReportProgressParams
+
+ $Results = @()
+
+ foreach ($User in $Users.GetEnumerator()) {
+ $ADUser = Get-ADUser -Identity $User.Name -Properties @(
+ 'physicalDeliveryOfficeName', 'Office', 'Department', 'Company', 'City', 'telephoneNumber'
+ )
+
+ $UserObject = New-Object psobject -Property @{
+ UserId = $User.Name
+ Email = $ADUser.UserPrincipalName
+ Phone = $ADUser.telephoneNumber
+ FullName = $ADUser.Name
+ Enabled = $ADUser.Enabled
+ PhysicalOffice = $ADUser.physicalDeliveryOfficeName
+ Office = $ADUser.Office
+ Department = $ADUser.Department
+ Company = $ADUser.Company
+ City = $ADUser.City
+ }
+
+ foreach ($Group in $Groups) {
+ $UserObject | Add-Member -MemberType NoteProperty -Name $Group.Name -Value ''
+
+ if ($User.Value.ContainsKey($Group.Name)) {
+ $UserObject.($Group.Name) = 'x'
+ }
+ }
+
+ $Results += $UserObject
+ }
+ }
+
+ end {
+ if ($SaveToCsv) { $Results | Export-Csv -Path $SaveToCsv -NoTypeInformation }
+ $Results
+ }
+}
\ No newline at end of file
diff --git a/AD/Get-DisabledUser.ps1 b/AD/Get-DisabledUser.ps1
new file mode 100644
index 0000000..2990c1d
--- /dev/null
+++ b/AD/Get-DisabledUser.ps1
@@ -0,0 +1,43 @@
+#Requires –Version 3
+
+function Get-DisabledUser {
+ param (
+ [Parameter(Mandatory = $true)]
+ [string]$UsersFilePath,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateSet('Email', 'FullName', 'UserID')]
+ [string]$ListType = 'Email'
+ )
+
+ begin {
+ Import-Module ActiveDirectory
+
+ $Users = Get-Content -Path $UsersFilePath
+ $ADUsers = Get-ADUser -SearchBase 'OU=Users,DC=company,DC=LOCAL' -Filter *
+ $Results = @()
+ }
+
+ process {
+ foreach ($User in $Users) {
+ switch ($ListType) {
+ 'Email' { $SearchProperty = 'UserPrincipalName' }
+ 'FullName' { $SearchProperty = 'Name' }
+ 'UserID' { $SearchProperty = 'SamAccountName' }
+ }
+
+ $FoundUser = $ADUsers | Where-Object { $_.$SearchProperty -eq $User }
+
+ $Results += New-Object -TypeName PSObject -Property @{
+ SearchProperty = $User
+ UserPrincipalName = $FoundUser.UserPrincipalName
+ Name = $FoundUser.Name
+ SamAccountName = $FoundUser.SamAccountName
+ Enabled = if ($FoundUser.Enabled) { $true } else { $false }
+ }
+ }
+ $Results | Sort-Object Enabled | Format-Table -AutoSize
+ }
+}
+
+Get-DisabledUser -UsersFilePath C:\TestUsers.txt -ListType Email
\ No newline at end of file
diff --git a/AD/Reset-ADLastPasswordSetTime.ps1 b/AD/Reset-ADLastPasswordSetTime.ps1
new file mode 100644
index 0000000..816fba6
--- /dev/null
+++ b/AD/Reset-ADLastPasswordSetTime.ps1
@@ -0,0 +1,5 @@
+$creds = Get-Credential
+
+$Me = Get-ADUser -Filter * | Where-Object -Property SamAccountName -Like userid*
+$Me | Set-ADUser -ChangePasswordAtLogon $true -Credential $creds
+$Me | Set-ADUser -ChangePasswordAtLogon $false -Credential $creds
\ No newline at end of file
diff --git a/AD/Test-Credential.ps1 b/AD/Test-Credential.ps1
new file mode 100644
index 0000000..ac4b048
--- /dev/null
+++ b/AD/Test-Credential.ps1
@@ -0,0 +1,65 @@
+<#
+.DESCRIPTION
+ Simulates an Authentication Request in a Domain envrionment using a PSCredential Object. Returns $true if both Username and Password pair are valid.
+.VERSION
+ 1.3
+.GUID
+ 6a18515f-73d3-4fb4-884f-412395aa5054
+.AUTHOR
+ Thomas Malkewitz @dotps1
+.TAGS
+ PSCredential, Credential
+.RELEASENOTES
+ Updated $Domain default value to $Credential.GetNetworkCredential().Domain.
+ Added support for multipul credential objects to be passed into $Credential.
+.PROJECTURI
+ http://dotps1.github.io
+.NOTES
+ Slight modifications by Nick
+ #>
+
+Function Test-Credential {
+ [OutputType([Bool])]
+
+ Param (
+ [Parameter(
+ Mandatory = $true,
+ ValueFromPipeLine = $true,
+ ValueFromPipelineByPropertyName = $true
+ )]
+ [Alias('PSCredential')]
+ [ValidateNotNull()]
+ [System.Management.Automation.PSCredential]
+ [System.Management.Automation.Credential()]
+ $Credential,
+
+ [Parameter()]
+ [String]
+ $Domain = $Credential.GetNetworkCredential().Domain
+ )
+
+ Begin {
+ [System.Reflection.Assembly]::LoadWithPartialName("System.DirectoryServices.AccountManagement") |
+ Out-Null
+
+ $principalContext = New-Object System.DirectoryServices.AccountManagement.PrincipalContext(
+ [System.DirectoryServices.AccountManagement.ContextType]::Domain, $Domain
+ )
+ }
+
+ Process {
+ foreach ($item in $Credential) {
+ $networkCredential = $Credential.GetNetworkCredential()
+
+ Write-Output -InputObject $(
+ $principalContext.ValidateCredentials(
+ $networkCredential.UserName, $networkCredential.Password
+ )
+ )
+ }
+ }
+
+ End {
+ $principalContext.Dispose()
+ }
+}
\ No newline at end of file
diff --git a/AD/Update-ADMFARegistrationGroup.ps1 b/AD/Update-ADMFARegistrationGroup.ps1
new file mode 100644
index 0000000..4c68b9b
--- /dev/null
+++ b/AD/Update-ADMFARegistrationGroup.ps1
@@ -0,0 +1,18 @@
+Import-Module ActiveDirectory
+
+# Get all MSO users and their MFA status, convert into AD users
+$MFAUsers = . "MSO\Get-MFAEnabledUser.ps1"
+$RegisteredUsers = $MFAUsers | Where-Object -Property DefaultMethod -NE $null | ForEach-Object {
+ Get-ADUser -Filter "UserPrincipalName -eq '$($_.UserPrincipalName)'"
+}
+
+# Get the group that holds unregistered MFA users and the users within
+$UnregisteredGroup = Get-ADGroup -Identity 'MFA Registration Incomplete'
+$UnregisteredUsers = $UnregisteredGroup | Get-ADGroupMember | Get-ADUser
+
+# Find users in the group that have a default MFA method
+$UsersToRemove = $UnregisteredUsers | Compare-Object -ReferenceObject $RegisteredUsers -IncludeEqual |
+Where-Object -Property SideIndicator -eq '=='
+
+# Remove users with a default MFA method
+Remove-ADGroupMember -Identity $UnregisteredGroup -Members $UsersToRemove.InputObject
\ No newline at end of file
diff --git a/Application Management/Get-AADConnectVersion.ps1 b/Application Management/Get-AADConnectVersion.ps1
new file mode 100644
index 0000000..a48aff6
--- /dev/null
+++ b/Application Management/Get-AADConnectVersion.ps1
@@ -0,0 +1,7 @@
+$Creds = Get-Credential
+$DirSyncServer = 'dirsync01'
+
+Invoke-Command -ComputerName $DirSyncServer -Credential $Creds -ScriptBlock {
+ $RegKey = "HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\*"
+ Get-ItemProperty -Path $RegKey | Where-Object -Property DisplayName -EQ 'Microsoft Azure AD Connect'
+} | Select-Object -Property DisplayName, PSPath, Version, DisplayVersion | Format-List
\ No newline at end of file
diff --git a/CaseWare/New-CWSyncServer.ps1 b/CaseWare/New-CWSyncServer.ps1
new file mode 100644
index 0000000..ce0f1bd
--- /dev/null
+++ b/CaseWare/New-CWSyncServer.ps1
@@ -0,0 +1,41 @@
+function New-CWSyncServer {
+ <#
+ .DESCRIPTION
+ Add SmartSync server to CaseWare Working Papers.
+
+ .PARAMETER HostName
+ Host name of SmartSync server.
+
+ .PARAMETER Label
+ Friendly name of SmartSync server as it will appear in CaseWare Working Papers.
+
+ .EXAMPLE
+ New-CWSyncServer site01.company.com
+
+ .NOTES
+ Created by Nick Rodriguez
+ Adds new SmartSync server to registry that will appear in CaseWare Working Papers.
+ #>
+ [CmdletBinding(SupportsShouldProcess = $true)]
+ Param(
+ [Parameter(Mandatory = $true)]
+ [String]
+ $HostName,
+
+ [Parameter(Mandatory = $false)]
+ [String]
+ $FriendlyName
+ )
+
+ # Create the site key
+ $RegPath = 'HKCU:\SOFTWARE\CaseWare International\Working Papers\*\SyncServer'
+ if (-not (Test-Path -Path "$RegPath\{$HostName}")) { New-Item -Path $RegPath -Name "{$HostName}" }
+
+ # Set the two site key properties
+ Set-ItemProperty -Path "$RegPath\{$HostName}" -Name Host -Value $HostName
+ Set-ItemProperty -Path "$RegPath\{$HostName}" -Name Label -Value $FriendlyName
+
+ # Validate with SCCM friendly exit codes
+ $SiteKey = Get-ItemProperty -Path "$RegPath\{$HostName}"
+ if ($SiteKey.Host -eq $HostName -and $SiteKey.Label -eq $FriendlyName) { exit 0 } else { exit 999 }
+}
\ No newline at end of file
diff --git a/Data/Convert-RobocopyExitCode.ps1 b/Data/Convert-RobocopyExitCode.ps1
new file mode 100644
index 0000000..ec2a794
--- /dev/null
+++ b/Data/Convert-RobocopyExitCode.ps1
@@ -0,0 +1,22 @@
+function Convert-RobocopyExitCode ($ExitCode) {
+ switch ($ExitCode) {
+ 16 {'***FATAL ERROR***'}
+ 15 {'OKCOPY + FAIL + MISMATCHES + XTRA'}
+ 14 {'FAIL + MISMATCHES + XTRA'}
+ 13 {'OKCOPY + FAIL + MISMATCHES'}
+ 12 {'FAIL + MISMATCHES'}
+ 11 {'OKCOPY + FAIL + XTRA'}
+ 10 {'FAIL + XTRA'}
+ 9 {'OKCOPY + FAIL'}
+ 8 {'FAIL'}
+ 7 {'OKCOPY + MISMATCHES + XTRA'}
+ 6 {'MISMATCHES + XTRA'}
+ 5 {'OKCOPY + MISMATCHES'}
+ 4 {'MISMATCHES'}
+ 3 {'OKCOPY + XTRA'}
+ 2 {'XTRA'}
+ 1 {'OKCOPY'}
+ 0 {'No Change'}
+ default {'Unknown'}
+ }
+}
\ No newline at end of file
diff --git a/Data/Convert-TableToHTML.ps1 b/Data/Convert-TableToHTML.ps1
new file mode 100644
index 0000000..b17a8a1
--- /dev/null
+++ b/Data/Convert-TableToHTML.ps1
@@ -0,0 +1,22 @@
+# You can embed the css in the script or alternatively you can reference a css file
+
+# File reference
+$Message = $Table | ConvertTo-Html -CssUri $CSSFilePath | Out-String
+
+# Embed
+$CSS = @"
+
+"@
+$Message = $Table | ConvertTo-Html -Head $CSS | Out-String
+
+# And then use this to send the email
+Send-MailMessage -Body $Message -BodyAsHtml -From 'Alerts@company.com' `
+ -SmtpServer smtp.company.local -Subject 'Report' -To 'me@company.com'
\ No newline at end of file
diff --git a/Data/ConvertFrom-Base64Image.ps1 b/Data/ConvertFrom-Base64Image.ps1
new file mode 100644
index 0000000..83daa76
--- /dev/null
+++ b/Data/ConvertFrom-Base64Image.ps1
@@ -0,0 +1,3 @@
+$Base64Icon = ''
+$IconStream = [System.Convert]::FromBase64String($Base64Icon)
+$IconBMP = [System.Drawing.Image]::FromStream($IconStream)
\ No newline at end of file
diff --git a/Data/ConvertTo-Base64Image.ps1 b/Data/ConvertTo-Base64Image.ps1
new file mode 100644
index 0000000..1706d70
--- /dev/null
+++ b/Data/ConvertTo-Base64Image.ps1
@@ -0,0 +1,5 @@
+[System.Reflection.Assembly]::LoadWithPartialName('System.Windows.Forms') | Out-Null
+$OpenFileDialog = New-Object System.Windows.Forms.OpenFileDialog
+$OpenFileDialog.ShowDialog() | Out-Null
+$Image = Get-Item $OpenFileDialog.FileName
+[System.Convert]::ToBase64String((Get-Content $Image -Encoding Byte)) >> .\EncodedImage.txt
\ No newline at end of file
diff --git a/Data/ConvertTo-Icon.ps1 b/Data/ConvertTo-Icon.ps1
new file mode 100644
index 0000000..6361c8b
--- /dev/null
+++ b/Data/ConvertTo-Icon.ps1
@@ -0,0 +1,25 @@
+[CmdletBinding()]
+param(
+ [Parameter(
+ Mandatory = $true,
+ ValueFromPipeline = $true,
+ ValueFromPipelineByPropertyName = $true
+ )]
+ [ValidateScript({
+ if (Test-Path -Path $_ -PathType Leaf) {
+ $true
+ } else {
+ throw "[$_] is not a valid file."
+ $false
+ }
+ })]
+ [string[]]$FilePath
+)
+
+foreach ($Path in $FilePath) {
+ Add-Type -AssemblyName System.Drawing
+
+ $File = Get-Item -Path $Path
+ $Icon = [System.Drawing.Icon]::ExtractAssociatedIcon($File.FullName)
+ $Icon.ToBitmap().Save($File.FullName.Replace($File.Extension, '.ico'))
+}
\ No newline at end of file
diff --git a/Data/Get-MD5Checksum.ps1 b/Data/Get-MD5Checksum.ps1
new file mode 100644
index 0000000..20024d1
--- /dev/null
+++ b/Data/Get-MD5Checksum.ps1
@@ -0,0 +1,10 @@
+# If the content is a string:
+$someString = "Hello World!"
+$md5 = new-object -TypeName System.Security.Cryptography.MD5CryptoServiceProvider
+$utf8 = new-object -TypeName System.Text.UTF8Encoding
+$hash = [System.BitConverter]::ToString($md5.ComputeHash($utf8.GetBytes($someString)))
+
+# If the content is a file:
+$someFilePath = "C:\foo.txt"
+$md5 = New-Object -TypeName System.Security.Cryptography.MD5CryptoServiceProvider
+$hash = [System.BitConverter]::ToString($md5.ComputeHash([System.IO.File]::ReadAllBytes($someFilePath)))
\ No newline at end of file
diff --git a/Exchange/Enter-EOSession.ps1 b/Exchange/Enter-EOSession.ps1
new file mode 100644
index 0000000..9ed7349
--- /dev/null
+++ b/Exchange/Enter-EOSession.ps1
@@ -0,0 +1,7 @@
+# If you get an Access Denied message but you're a member of Exchange Online Admins,
+# make sure you don't have MFA enabled
+$ExchangeOnlineSession = New-PSSession -ConfigurationName Microsoft.Exchange `
+ -ConnectionUri https://outlook.office365.com/powershell-liveid/ `
+ -Credential (Get-Credential) -Authentication Basic -AllowRedirection
+
+Import-PSSession $ExchangeOnlineSession
\ No newline at end of file
diff --git a/Exchange/GalSync.psm1 b/Exchange/GalSync.psm1
new file mode 100644
index 0000000..45774bc
--- /dev/null
+++ b/Exchange/GalSync.psm1
@@ -0,0 +1,277 @@
+function Sync-Gal {
+ <#
+ .DESCRIPTION
+ Does a one-way sync between two Exchange Online Gal's.
+
+ .PARAMETER PrimaryTenantCreds
+ Credentials to login to the primary tenant.
+
+ .PARAMETER SecondaryTenantCreds
+ Credentials to login to the secondary tenant.
+
+ .PARAMETER AsJob
+ Enables the updates to be run as PS Jobs.
+
+ .PARAMETER Jobs
+ The number of PS Jobs to create to run the updates. Limited to 3 because of an O365 default max concurrent connection.
+
+ .PARAMETER ContactLimit
+ Number of contacts to sync. Default is Unlimited. Useful for doing quick tests.
+
+ .EXAMPLE
+ ./Sync-Gal.ps1 -PrimaryTenantCreds (Get-Credential) -SecondaryTenantCreds (Get-Credential) -AsJob -Jobs 3
+
+ .EXAMPLE
+ ./Sync-Gal.ps1
+
+ .NOTES
+ Created by Nick Rodriguez
+ Syncs the Gal of Exchange Online across two Office 365 tenants
+ Note that this will break the creation of external user objects until MS addresses a known issue:
+ https://products.office.com/en-us/business/office-365-roadmap?filters=&featureid=72273
+ #>
+ [CmdletBinding(
+ SupportsShouldProcess = $true,
+ DefaultParameterSetName = 'Synchronous'
+ )]
+ Param(
+ [Parameter(Mandatory = $true)]
+ [PSCredential]
+ $PrimaryTenantCreds,
+
+ [Parameter(Mandatory = $true)]
+ [PSCredential]
+ $SecondaryTenantCreds,
+
+ [Parameter(
+ Mandatory = $false,
+ ParameterSetName = 'Asynchronous'
+ )]
+ [Switch]
+ $AsJob,
+
+ [Parameter(
+ Mandatory = $false,
+ ParameterSetName = 'Asynchronous'
+ )]
+ [ValidateRange(1, 3)]
+ [Int]
+ $Jobs = 2,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateRange(0, [Int]::MaxValue)]
+ [Int]
+ $ContactLimit = 0
+ )
+
+ begin {
+ # Log everything
+ $LogDirectory = (New-Item -ItemType Directory "C:\powershell-scripts\Exchange Online\Logs" -Force).FullName
+ $Date = (Get-Date).ToString('yyyyMMdd-HHmm')
+ Start-Transcript -Path "$LogDirectory\$($MyInvocation.MyCommand.Name)-$Date.log"
+ }
+
+ process {
+ # Create Exchange Online PowerShell session for primary tenant
+ $PrimaryTenantEOSession = New-EOSession -Credential $PrimaryTenantCreds
+
+ # Enter session on primary tenant
+ Import-PSSession $PrimaryTenantEOSession
+
+ # Get all Gal recipients using the primary filter
+ $GalFilter = (Get-GlobalAddressList).RecipientFilter
+ $ResultSizeLimit = if ($ContactLimit -eq 0) { 'Unlimited' } else { $ContactLimit }
+ $Gal = Get-Recipient -ResultSize $ResultSizeLimit -Filter $GalFilter
+
+ # Export Gal to Csv file
+ $Gal | Export-Csv -Path "$LogDirectory\Gal.csv" -NoTypeInformation -Force
+
+ # Remove session on primary tenant
+ Remove-PSSession -Session $PrimaryTenantEOSession
+
+ # Create/Update contact for each Gal entry
+ # If Jobs param specified, break up the list into smaller lists that can be started as jobs
+ if ($AsJob) {
+ $ContactLists = @{}
+ $Count = 0
+
+ # Separate the contacts into smaller lists
+ $Gal | ForEach-Object {
+ $ContactLists[$Count % $Jobs] += @($_)
+ $Count++
+ }
+
+ # Create a job for each sublist of contacts
+ foreach ($ContactList in $ContactLists.Values) {
+ Start-Job -ArgumentList $SecondaryTenantCreds, $ContactList -ScriptBlock {
+ # Create Exchange Online PS session
+ $SecondaryTenantEOSession = New-EOSession -Credential $args[0]
+
+ # Enter session on secondary tenant
+ Import-PSSession $SecondaryTenantEOSession
+
+ Update-GalContact -Gal $args[1]
+
+ # Remove session on secondary tenant
+ Remove-PSSession -Session $SecondaryTenantEOSession
+ } -InitializationScript { Import-Module 'C:\powershell-scripts\Exchange Online\GalSync.psm1' }
+ }
+
+ # Wait for all jobs to finish then receive and remove the jobs
+ Get-Job | Wait-Job | Receive-Job
+ Get-Job | Remove-Job
+ } else {
+ # Create Exchange Online PS session
+ $SecondaryTenantEOSession = New-EOSession -Credential $SecondaryTenantCreds
+
+ # Enter session on secondary tenant
+ Import-PSSession $SecondaryTenantEOSession
+
+ Update-GalContact -Gal $Gal
+
+ # Remove session on secondary tenant
+ Remove-PSSession -Session $SecondaryTenantEOSession
+ }
+ }
+
+ end {
+ # Remove any lingering PSSessions and stop the logging
+ Get-PSSession | Remove-PSSession
+ Stop-Transcript
+ }
+}
+
+function New-EOSession {
+ <#
+ .DESCRIPTION
+ Create a new Exchange Online PowerShell session
+
+ .PARAMETER Credential
+ The credentials to use for the session.
+
+ .EXAMPLE
+ New-EOSession -Credential $creds
+
+ .NOTES
+ Created by Nick Rodriguez
+ #>
+ [CmdletBinding(SupportsShouldProcess = $true)]
+ Param(
+ [Parameter(Mandatory = $true)]
+ [PSCredential]
+ $Credential
+ )
+
+ New-PSSession -ConfigurationName Microsoft.Exchange -Authentication Basic -AllowRedirection `
+ -ConnectionUri https://outlook.office365.com/powershell-liveid/ -Credential $Credential
+}
+
+function Update-GalContact {
+ <#
+ .DESCRIPTION
+ Takes an array of recipients and updates the Gal.
+
+ .PARAMETER Gal
+ The recipient or list of reipients to update.
+
+ .EXAMPLE
+ Update-GalContact -Gal $ExternalGal
+
+ .NOTES
+ Created by Nick Rodriguez
+ #>
+ [CmdletBinding(SupportsShouldProcess = $true)]
+ Param(
+ [PSObject[]]$Gal
+ )
+
+ foreach ($Recipient in $Gal) {
+ # Create a new contact if one doesn't exist
+ if (Get-MailContact -Identity $Recipient.Name) {
+ Write-Host "Contact $($Recipient.Name) already exists."
+ } else {
+ try {
+ New-MailContact `
+ -ExternalEmailAddress $Recipient.PrimarySmtpAddress `
+ -Name $Recipient.Name `
+ -FirstName $Recipient.FirstName `
+ -LastName $Recipient.LastName `
+ -DisplayName $Recipient.DisplayName `
+ -Alias $Recipient.Alias
+ Write-Host "New contact created for $($Recipient.Name)"
+ } catch {
+ Write-Host "Error creating new contact for $($Recipient.Name): $_"
+ }
+ }
+
+ try {
+ # Update mail contact properties
+ Set-MailContact `
+ -Identity $Recipient.Name `
+ -ExternalEmailAddress $Recipient.PrimarySmtpAddress `
+ -Name $Recipient.Name `
+ -DisplayName $Recipient.DisplayName `
+ -Alias $Recipient.Alias `
+ -CustomAttribute1 $Recipient.CustomAttribute1 `
+ -CustomAttribute2 $Recipient.CustomAttribute2 `
+ -CustomAttribute3 $Recipient.CustomAttribute3 `
+ -CustomAttribute4 $Recipient.CustomAttribute4 `
+ -CustomAttribute5 $Recipient.CustomAttribute5 `
+ -CustomAttribute6 $Recipient.CustomAttribute6 `
+ -CustomAttribute7 $Recipient.CustomAttribute7 `
+ -CustomAttribute8 $Recipient.CustomAttribute8 `
+ -CustomAttribute9 $Recipient.CustomAttribute9 `
+ -CustomAttribute10 $Recipient.CustomAttribute10 `
+ -CustomAttribute11 $Recipient.CustomAttribute11 `
+ -CustomAttribute12 $Recipient.CustomAttribute12 `
+ -CustomAttribute13 $Recipient.CustomAttribute13 `
+ -CustomAttribute14 $Recipient.CustomAttribute14 `
+ -CustomAttribute15 $Recipient.CustomAttribute15 `
+ -ExtensionCustomAttribute1 $Recipient.ExtensionCustomAttribute1 `
+ -ExtensionCustomAttribute2 $Recipient.ExtensionCustomAttribute2 `
+ -ExtensionCustomAttribute3 $Recipient.ExtensionCustomAttribute3 `
+ -ExtensionCustomAttribute4 $Recipient.ExtensionCustomAttribute4 `
+ -ExtensionCustomAttribute5 $Recipient.ExtensionCustomAttribute5 `
+ | Out-Null
+
+ # Update Windows Email Address only if it's populated
+ if ($Recipient.WindowsLiveID -ne '') {
+ Set-MailContact -Identity $Recipient.Name -WindowsEmailAddress $Recipient.WindowsLiveID | Out-Null
+ }
+ } catch {
+ Write-Host "Error updating mail contact info for $($Recipient.Name): $_"
+ }
+
+ try {
+ # Update contact properties
+ Set-Contact `
+ -Identity $Recipient.Name `
+ -FirstName $Recipient.FirstName `
+ -LastName $Recipient.LastName `
+ -Department $Recipient.Department `
+ -Company $Recipient.Company `
+ -Phone $Recipient.Phone `
+ -HomePhone $Recipient.HomePhone `
+ -OtherHomePhone $Recipient.OtherHomePhone `
+ -MobilePhone $Recipient.MobilePhone `
+ -OtherTelephone $Recipient.OtherTelephone `
+ -Pager $Recipient.Pager `
+ -Fax $Recipient.Fax `
+ -OtherFax $Recipient.OtherFax `
+ -Office $Recipient.Office `
+ -CountryOrRegion $Recipient.UsageLocation `
+ -StreetAddress $Recipient.StreetAddress `
+ -City $Recipient.City `
+ -StateOrProvince $Recipient.StateOrProvince `
+ -PostalCode $Recipient.PostalCode `
+ -PostOfficeBox $Recipient.PostOfficeBox `
+ -Title $Recipient.Title `
+ -Manager $Recipient.Manager `
+ -AssistantName $Recipient.AssistantName `
+ -Notes $Recipient.Notes `
+ | Out-Null
+ } catch {
+ Write-Host "Error updating contact info for $($Recipient.Name): $_"
+ }
+ }
+}
\ No newline at end of file
diff --git a/Exchange/Get-AllMailboxSizes.ps1 b/Exchange/Get-AllMailboxSizes.ps1
new file mode 100644
index 0000000..60454be
--- /dev/null
+++ b/Exchange/Get-AllMailboxSizes.ps1
@@ -0,0 +1,25 @@
+$Creds = Get-Credential
+
+# If you get an Access Denied message but you're a member of Exchange Online Admins,
+# make sure you don't have MFA enabled
+$ExchangeOnlineSession = New-PSSession -ConfigurationName Microsoft.Exchange `
+ -ConnectionUri https://outlook.office365.com/powershell-liveid/ `
+ -Credential $Creds -Authentication Basic -AllowRedirection
+
+Import-PSSession $ExchangeOnlineSession
+
+$LogDirectory = (New-Item -ItemType Directory "$PSScriptRoot\Logs" -Force).FullName
+$Date = (Get-Date).ToString('yyyyMMdd-HHmm')
+$CsvPath = "$LogDirectory\$($MyInvocation.MyCommand.Name)-$Date.csv"
+
+Get-Mailbox -ResultSize Unlimited |
+ Get-MailboxStatistics |
+ Select-Object DisplayName, StorageLimitStatus, ItemCount, `
+ @{
+ name = "TotalItemSize (MB)"
+ expression = { [math]::Round( `
+ ($_.TotalItemSize.ToString().Split("(")[1].Split(" ")[0].Replace(",","")/1MB),2)
+ }
+ } |
+ Sort-Object "TotalItemSize (MB)" -Descending |
+ Export-CSV $CsvPath -NoTypeInformation
\ No newline at end of file
diff --git a/Exchange/Set-MSOMailboxAuditing.ps1 b/Exchange/Set-MSOMailboxAuditing.ps1
new file mode 100644
index 0000000..bb76f61
--- /dev/null
+++ b/Exchange/Set-MSOMailboxAuditing.ps1
@@ -0,0 +1,91 @@
+# Start a transcript of everything we do
+$LogDirectory = (New-Item -ItemType Directory "$PSScriptRoot\Logs" -Force).FullName
+$Date = (Get-Date).ToString('yyyyMMdd-HHmm')
+$LogPath = "$LogDirectory\$($MyInvocation.MyCommand.Name)-$Date.log"
+$ResultsPath = "$LogDirectory\$($MyInvocation.MyCommand.Name)-$Date.csv"
+
+# Connect to Exchange Online
+$Creds = Get-Credential
+$ExchangeOnlineSession = New-PSSession -ConfigurationName Microsoft.Exchange `
+ -ConnectionUri https://outlook.office365.com/powershell-liveid/ `
+ -Credential $Creds -Authentication Basic -AllowRedirection
+Import-PSSession $ExchangeOnlineSession
+
+Start-Transcript -Path $LogPath
+
+# Audit options
+$AuditOwnerOptions = @(
+ 'Create', 'HardDelete', 'MailboxLogin', 'Move', 'MoveToDeletedItems',
+ 'SoftDelete', 'Update'
+)
+
+$AuditAdminOptions = @(
+ 'Copy', 'Create', 'FolderBind', 'HardDelete', 'MessageBind', 'Move',
+ 'MoveToDeletedItems', 'SendAs', 'SendOnBehalf', 'SoftDelete', 'Update'
+)
+
+$AuditDelegateOptions = @(
+ 'Create', 'FolderBind', 'HardDelete', 'Move', 'MoveToDeletedItems',
+ 'SendAs', 'SendOnBehalf', 'SoftDelete', 'Update'
+)
+
+# Keep track of how many accounts we fix
+$BadAccounts = 0
+
+# Get all users and enable auditing options for their mailboxes
+Get-Mailbox -ResultSize Unlimited -RecipientTypeDetails UserMailbox | ForEach-Object {
+ $Params = @{ 'Identity' = $_.UserPrincipalName }
+
+ # Verify audit options are enabled
+ if (-not $_.AuditEnabled) {
+ Write-Output "$($_.UserPrincipalName) - enabling audit."
+ $Params.Add('AuditEnabled', $true)
+ }
+
+ # Verify audit owner options are correct
+ if (Compare-Object -ReferenceObject $_.AuditOwner -DifferenceObject $AuditOwnerOptions) {
+ Write-Output "$($_.UserPrincipalName) - resetting Audit Owner options."
+ $Params.Add('AuditOwner', $AuditOwnerOptions)
+ }
+
+ # Verify audit admin options are correct
+ if (Compare-Object -ReferenceObject $_.AuditAdmin -DifferenceObject $AuditAdminOptions) {
+ Write-Output "$($_.UserPrincipalName) - resetting Audit Admin options."
+ $Params.Add('AuditAdmin', $AuditAdminOptions)
+ }
+
+ # Verify audit delegate options are correct
+ if (Compare-Object -ReferenceObject $_.AuditDelegate -DifferenceObject $AuditDelegateOptions) {
+ Write-Output "$($_.UserPrincipalName) - resetting Audit Delegate options."
+ $Params.Add('AuditDelegate', $AuditDelegateOptions)
+ }
+
+ # Update user options if any don't match out settings
+ if ($Params.Count -gt 1) {
+ Write-Output "$($_.UserPrincipalName) - setting mailbox options."
+ try { Set-Mailbox @Params -WhatIf } catch { $_ }
+ $BadAccounts++
+ }
+}
+
+Write-Output "Audit options were set on $BadAccounts mailboxes."
+
+# Save resulting audit rules of all users to csv
+Get-Mailbox -ResultSize Unlimited -RecipientTypeDetails UserMailbox |
+ Select-Object PrimarySmtpAddress, Name, Audit* |
+ Export-Csv -Path $ResultsPath -NoTypeInformation
+
+# Disconnect from Exchange Online
+Remove-PSSession $ExchangeOnlineSession
+Stop-Transcript
+
+# Send results via email
+$Params = @{
+ 'Body' = Get-Content -Path $LogPath | Out-String
+ 'From' = 'Alerts@company.com'
+ 'SmtpServer' = 'smtp.company.local'
+ 'Subject' = 'Enable Mailbox Auditing'
+ 'To' = 'person@company.com'
+ 'Attachments' = $ResultsPath
+}
+Send-MailMessage @Params
\ No newline at end of file
diff --git a/File Management/Archive-DuplicateFile.ps1 b/File Management/Archive-DuplicateFile.ps1
index ab4f33f..f22bec6 100644
--- a/File Management/Archive-DuplicateFile.ps1
+++ b/File Management/Archive-DuplicateFile.ps1
@@ -31,46 +31,68 @@ function Archive-DuplicateFile {
Write-Verbose "Getting all files within [$Dir]..."
Get-ChildItem -Path $Dir -File -Recurse -Exclude $DupeDir -Force -ErrorAction Continue | Where-Object {
+ # Don't search for duplicates within the directory we're archiving to
$_.FullName -notlike "$DupeDir\*"
} | ForEach-Object {
+ Write-Host $_.FullName
+
# Check if the file name exists in our list
if ($AllItems.ContainsKey($_.Name)) {
+
+ # Create an object with details about the item we're about to archive
$ArchiveFile = New-Object -TypeName psobject -Property @{
Name = $_.Name
- Path = $_.FullName
+ OriginalPath = $_.FullName
+ ArchivePath = ''
LastWriteTime = $_.LastWriteTime
+ Error = ''
}
- $CurrLastWriteTime = $AllItems.($_.Name)[0]
- $CurrFullName = $AllItems.($_.Name)[1]
+ $PreviousLastWriteTime = $AllItems.($ArchiveFile.Name)[0]
+ $PreviousFullName = $AllItems.($ArchiveFile.Name)[1]
# If this file is newer than what was previously found, archive old and add this to list
- if ($_.LastWriteTime -gt $CurrLastWriteTime) {
- Write-Verbose "[$($_.Name)] found in list already, this one is newer, archiving older copy..."
+ if ($ArchiveFile.LastWriteTime -gt $PreviousLastWriteTime) {
+ Write-Verbose "[$($ArchiveFile.Name)] found already, this one is newer, archiving [$PreviousFullName]..."
- If ($PSCmdlet.ShouldProcess($CurrFullName, 'Archive Item')) {
- # Archive the old item and log it
- $ArchiveFile.LastWriteTime = $CurrLastWriteTime
- $ArchiveFile.Path = $CurrFullName
- $ArchiveFile | Export-Csv -Path $Log -Append -NoTypeInformation
- Move-Item -Path $CurrFullName -Destination $DupeDir
- }
+ if ($PSCmdlet.ShouldProcess($PreviousFullName, 'Archive Item')) {
+ # Archive the old item and add the new item to the list
+ $ArchiveFile.ArchivePath = $PreviousFullName.Replace($Dir, $DupeDir)
+ try {
+ New-Item -Path $ArchiveFile.ArchivePath.TrimEnd($_.Name) -ItemType Directory -Force
+ Move-Item -Path $PreviousFullName -Destination $ArchiveFile.ArchivePath -Force
+ } catch {
+ $ArchiveFile.Error = $_.Exception.Message
+ }
+ $ArchiveFile.LastWriteTime = $PreviousLastWriteTime
+ $ArchiveFile.OriginalPath = $PreviousFullName
- # Add our new item to the list
- $CurrLastWriteTime = $_.LastWriteTime
- $CurrFullName = $_.FullName
+ # Update what we have in our list
+ $AllItems.($ArchiveFile.Name)[0] = $PreviousLastWriteTime
+ $AllItems.($ArchiveFile.Name)[1] = $PreviousFullName
+ }
# If this file is older than what was previously found, archive it
} else {
- Write-Verbose "[$($_.Name)] found in list already, this one has older date, archiving..."
- If ($PSCmdlet.ShouldProcess($_.FullName, 'Archive Item')) {
- # Archive this item and log it
- $ArchiveFile | Export-Csv -Path $Log -Append -NoTypeInformation
- Move-Item -Path $_.FullName -Destination $DupeDir
+ Write-Verbose "[$($ArchiveFile.Name)] found already, this one is older, archiving [$($_.FullName)]..."
+
+ if ($PSCmdlet.ShouldProcess($_.FullName, 'Archive Item')) {
+ # Archive this item
+ $ArchiveFile.ArchivePath = $_.FullName.Replace($Dir, $DupeDir)
+ try {
+ New-Item -Path $ArchiveFile.ArchivePath.TrimEnd($_.Name) -ItemType Directory -Force
+ Move-Item -Path $_.FullName -Destination $ArchiveFile.ArchivePath -Force
+ } catch {
+ $ArchiveFile.Error = $_.Exception.Message
+ }
}
}
+
+ # Log it
+ $ArchiveFile | Export-Csv -Path $Log -Append -NoTypeInformation
+
} else {
- Write-Verbose "Adding [$($_.Name)] to list..."
+ # Item is unique so add it to the list
$AllItems.Add($_.Name, @($_.LastWriteTime, $_.FullName))
}
}
diff --git a/File Management/Compress-Log.ps1 b/File Management/Compress-Log.ps1
new file mode 100644
index 0000000..ec1ecc6
--- /dev/null
+++ b/File Management/Compress-Log.ps1
@@ -0,0 +1,59 @@
+#Requires -Version 5
+<#
+.DESCRIPTION
+Compress logs older than given time into an archive. Uses LastWriteTime property to determine age.
+
+.PARAMETER Path
+Path of log files.
+
+.PARAMETER Extension
+Extension of the log files to search for, default is 'log'.
+
+.PARAMETER DaysOld
+How many days old the file must be to get archived.
+
+.EXAMPLE
+./CompressLog.ps1 -Path ~\Downloads\logs -DaysOld 8
+Searches for log files (.log extension) in the given path that are older than 8 days old and compresses them.
+#>
+
+[CmdletBinding(SupportsShouldProcess = $true)]
+Param(
+ [Parameter(Mandatory = $true)]
+ [ValidateScript({ Test-Path -Path $_ -PathType Container })]
+ [string]$Path,
+
+ [Parameter(Mandatory = $false)]
+ [string]$Extension = 'log',
+
+ [Parameter(Mandatory = $false)]
+ [int]$DaysOld = 7
+)
+
+begin {
+ $CompareDate = (Get-Date).AddDays(-$DaysOld)
+ Write-Verbose "Threshold date: $CompareDate"
+
+ $ArchiveName = "LogsOlderThan-$($CompareDate.ToString('yyyy-MM-dd'))"
+ Write-Verbose "Archive name: $ArchiveName"
+}
+
+process {
+ # Get logs older than the compare date
+ $Files = Get-ChildItem -Path $Path -File -Filter "*.$Extension" |
+ Where-Object -Property LastWriteTime -LT $CompareDate
+
+ if ($Files.Count) {
+ Write-Verbose "$($Files.Count) old logs found"
+
+ # Create archive will old logs in it
+ if ($PSCmdlet.ShouldProcess($ArchiveName, 'New archive')) {
+ Compress-Archive -Path $Files.FullName -DestinationPath "$Path\$ArchiveName" -Update
+ }
+
+ # Delete old logs
+ $Files | Remove-Item
+ } else {
+ Write-Verbose 'No old logs found'
+ }
+}
\ No newline at end of file
diff --git a/Create-CAB.ps1 b/File Management/Create-CAB.ps1
old mode 100755
new mode 100644
similarity index 96%
rename from Create-CAB.ps1
rename to File Management/Create-CAB.ps1
index 3857d4b..6f24225
--- a/Create-CAB.ps1
+++ b/File Management/Create-CAB.ps1
@@ -1,63 +1,63 @@
-$project = Read-Host "Folder to convert to a CAB"
-$ddf = $project + ".ddf"
-$cab = $project + ".cab"
-
-# delete any preexisting ddf
-if (Test-Path $ddf) {
- Clear-Content $ddf
-} else {
- $ddf = New-Item -type file $ddf
-}
-
-# delete any preexisting cab
-if (Test-Path $cab) {
- Remove-Item $cab
-}
-
-# create the ddf
-Add-Content $ddf ".OPTION EXPLICIT"
-Add-Content $ddf ".Set CabinetNameTemplate=$cab"
-Add-Content $ddf ".Set Cabinet=on"
-Add-Content $ddf ".Set Compress=on`n`n"
-
-# add the manifest and any other files in the top most directory of the project
-Get-ChildItem $project -File | ForEach-Object {
- $trash, $file = $_.FullName -split $project, 2
- $file = $project + $file
- $file = $([char]34) + $file + $([char]34)
-
- Add-Content $ddf $file
-}
-
-# create a new destination directory for each sub directory
-Get-ChildItem $project -Directory -Recurse | ForEach-Object {
-
- # if the directory has no files, skip it
- if ($_.GetFiles().Count) {
-
- $trash, $folder = $_.FullName -split $project, 2
- $folder = $folder.TrimStart('\')
- $folder = $([char]34) + $folder + $([char]34)
-
- Add-Content $ddf "`n`n.Set DestinationDir=$folder"
-
- # place the files for each sub directory under its destination directory entry
- Get-ChildItem $_.FullName -File | ForEach-Object {
- $trash, $file = $_.FullName -split $project, 2
- $file = $project + $file
- $file = $([char]34) + $file + $([char]34)
-
- Add-Content $ddf $file
- }
- }
-}
-
-# create the cab file
-Start-Process MakeCab -ArgumentList "/F ""$ddf""" -Wait
-
-# clean up
-Move-Item "disk1\$cab" .
-Remove-Item -Force "disk1"
-Remove-Item $ddf
-Remove-Item ".\setup.inf"
-Remove-Item ".\setup.rpt"
+$project = Read-Host "Folder to convert to a CAB"
+$ddf = $project + ".ddf"
+$cab = $project + ".cab"
+
+# delete any preexisting ddf
+if (Test-Path $ddf) {
+ Clear-Content $ddf
+} else {
+ $ddf = New-Item -type file $ddf
+}
+
+# delete any preexisting cab
+if (Test-Path $cab) {
+ Remove-Item $cab
+}
+
+# create the ddf
+Add-Content $ddf ".OPTION EXPLICIT"
+Add-Content $ddf ".Set CabinetNameTemplate=$cab"
+Add-Content $ddf ".Set Cabinet=on"
+Add-Content $ddf ".Set Compress=on`n`n"
+
+# add the manifest and any other files in the top most directory of the project
+Get-ChildItem $project -File | ForEach-Object {
+ $trash, $file = $_.FullName -split $project, 2
+ $file = $project + $file
+ $file = $([char]34) + $file + $([char]34)
+
+ Add-Content $ddf $file
+}
+
+# create a new destination directory for each sub directory
+Get-ChildItem $project -Directory -Recurse | ForEach-Object {
+
+ # if the directory has no files, skip it
+ if ($_.GetFiles().Count) {
+
+ $trash, $folder = $_.FullName -split $project, 2
+ $folder = $folder.TrimStart('\')
+ $folder = $([char]34) + $folder + $([char]34)
+
+ Add-Content $ddf "`n`n.Set DestinationDir=$folder"
+
+ # place the files for each sub directory under its destination directory entry
+ Get-ChildItem $_.FullName -File | ForEach-Object {
+ $trash, $file = $_.FullName -split $project, 2
+ $file = $project + $file
+ $file = $([char]34) + $file + $([char]34)
+
+ Add-Content $ddf $file
+ }
+ }
+}
+
+# create the cab file
+Start-Process MakeCab -ArgumentList "/F ""$ddf""" -Wait
+
+# clean up
+Move-Item "disk1\$cab" .
+Remove-Item -Force "disk1"
+Remove-Item $ddf
+Remove-Item ".\setup.inf"
+Remove-Item ".\setup.rpt"
diff --git a/Create-WSP.ps1 b/File Management/Create-WSP.ps1
old mode 100755
new mode 100644
similarity index 96%
rename from Create-WSP.ps1
rename to File Management/Create-WSP.ps1
index b0d5279..222e05d
--- a/Create-WSP.ps1
+++ b/File Management/Create-WSP.ps1
@@ -1,66 +1,66 @@
-$project = Read-Host "Folder to convert to a WSP"
-$ddf = $project + ".ddf"
-$cab = $project + ".cab"
-
-# delete any preexisting ddf
-if (Test-Path $ddf) {
- Clear-Content $ddf
-} else {
- $ddf = New-Item -type file $ddf
-}
-
-# delete any preexisting cab
-if (Test-Path $cab) {
- Remove-Item $cab
-}
-
-# create the ddf
-Add-Content $ddf ".OPTION EXPLICIT"
-Add-Content $ddf ".Set CabinetNameTemplate=$cab"
-Add-Content $ddf ".Set Cabinet=on"
-Add-Content $ddf ".Set Compress=on`n`n"
-
-# add the manifest and any other files in the top most directory of the project
-Get-ChildItem $project -File | ForEach-Object {
- $trash, $file = $_.FullName -split $project, 2
- $file = $project + $file
- $file = $([char]34) + $file + $([char]34)
-
- Add-Content $ddf $file
-}
-
-# create a new destination directory for each sub directory
-Get-ChildItem $project -Directory -Recurse | ForEach-Object {
-
- # if the directory has no files, skip it
- if ($_.GetFiles().Count) {
-
- $trash, $folder = $_.FullName -split $project, 2
- $folder = $folder.TrimStart('\')
- $folder = $([char]34) + $folder + $([char]34)
-
- Add-Content $ddf "`n`n.Set DestinationDir=$folder"
-
- # place the files for each sub directory under its destination directory entry
- Get-ChildItem $_.FullName -File | ForEach-Object {
- $trash, $file = $_.FullName -split $project, 2
- $file = $project + $file
- $file = $([char]34) + $file + $([char]34)
-
- Add-Content $ddf $file
- }
- }
-}
-
-# create the cab file
-Start-Process MakeCab -ArgumentList "/F ""$ddf""" -Wait
-
-# clean up
-Move-Item "disk1\$cab" .
-Remove-Item -Force "disk1"
-Remove-Item $ddf
-Remove-Item ".\setup.inf"
-Remove-Item ".\setup.rpt"
-
-# rename to wsp
-Rename-Item $cab ($project + ".wsp")
+$project = Read-Host "Folder to convert to a WSP"
+$ddf = $project + ".ddf"
+$cab = $project + ".cab"
+
+# delete any preexisting ddf
+if (Test-Path $ddf) {
+ Clear-Content $ddf
+} else {
+ $ddf = New-Item -type file $ddf
+}
+
+# delete any preexisting cab
+if (Test-Path $cab) {
+ Remove-Item $cab
+}
+
+# create the ddf
+Add-Content $ddf ".OPTION EXPLICIT"
+Add-Content $ddf ".Set CabinetNameTemplate=$cab"
+Add-Content $ddf ".Set Cabinet=on"
+Add-Content $ddf ".Set Compress=on`n`n"
+
+# add the manifest and any other files in the top most directory of the project
+Get-ChildItem $project -File | ForEach-Object {
+ $trash, $file = $_.FullName -split $project, 2
+ $file = $project + $file
+ $file = $([char]34) + $file + $([char]34)
+
+ Add-Content $ddf $file
+}
+
+# create a new destination directory for each sub directory
+Get-ChildItem $project -Directory -Recurse | ForEach-Object {
+
+ # if the directory has no files, skip it
+ if ($_.GetFiles().Count) {
+
+ $trash, $folder = $_.FullName -split $project, 2
+ $folder = $folder.TrimStart('\')
+ $folder = $([char]34) + $folder + $([char]34)
+
+ Add-Content $ddf "`n`n.Set DestinationDir=$folder"
+
+ # place the files for each sub directory under its destination directory entry
+ Get-ChildItem $_.FullName -File | ForEach-Object {
+ $trash, $file = $_.FullName -split $project, 2
+ $file = $project + $file
+ $file = $([char]34) + $file + $([char]34)
+
+ Add-Content $ddf $file
+ }
+ }
+}
+
+# create the cab file
+Start-Process MakeCab -ArgumentList "/F ""$ddf""" -Wait
+
+# clean up
+Move-Item "disk1\$cab" .
+Remove-Item -Force "disk1"
+Remove-Item $ddf
+Remove-Item ".\setup.inf"
+Remove-Item ".\setup.rpt"
+
+# rename to wsp
+Rename-Item $cab ($project + ".wsp")
diff --git a/File Management/Get-DirectoryLargerThan.ps1 b/File Management/Get-DirectoryLargerThan.ps1
new file mode 100644
index 0000000..148d42c
--- /dev/null
+++ b/File Management/Get-DirectoryLargerThan.ps1
@@ -0,0 +1,64 @@
+begin {
+ $RootDirectory = '\\server\users'
+ [long] $Script:Threshold = 5GB
+
+ function Format-Size {
+ param (
+ [Parameter(Mandatory = $true)]
+ [long] $Size
+ )
+
+ if ($Threshold -gt 1GB) {
+ "{0:N2}" -f ($Size / 1GB) + ' GB'
+ } else {
+ "{0:N2}" -f ($Size) + ' MB'
+ }
+ }
+
+ function Measure-Content {
+ param (
+ [Parameter(Mandatory = $true)]
+ [string] $DirectoryPath
+ )
+
+ $Contents = Get-ChildItem $DirectoryPath -Recurse -Force -ErrorAction Continue | Where-Object { $_.PSIsContainer -eq $false }
+ [long] ($Contents | Measure-Object -Property Length -Sum | Select-Object Sum).Sum
+ }
+
+
+ function Measure-Directory {
+ param (
+ [Parameter(Mandatory = $true)]
+ [string] $RootDirectory
+ )
+
+ Write-Output "Measuring children of $RootDirectory`n`n"
+
+ $LargeDirectories = @()
+
+ Get-ChildItem $RootDirectory | Where-Object { $_.PSIsContainer -eq $true } | ForEach-Object {
+ Write-Output "Measuring $_"
+
+ [long] $Size = Measure-Content $_.FullName
+
+ if ($Size -gt $Threshold) {
+ $LargeDirectories += New-Object psobject -Property @{
+ Size = Format-Size $Size
+ Path = $_.FullName
+ Directory = $_.Name
+ }
+ }
+ }
+
+ $Script:LargeDirectories
+ }
+}
+
+process {
+ Start-Transcript ".\logs\DirectoryLargerThan$(Format-Size $Threshold)-$(Get-Date -Format yyyy-MM-dd-HHmm).txt"
+ Measure-Directory $RootDirectory
+ Write-Output "`n`n`n`nThere are $($Results.Count) directories larger than $(Format-Size $Threshold)`n"
+ $LargeDirectories | Format-Table -AutoSize
+}
+
+end { Stop-Transcript }
\ No newline at end of file
diff --git a/File Management/Get-DuplicateFile.ps1 b/File Management/Get-DuplicateFile.ps1
new file mode 100644
index 0000000..c5c21c0
--- /dev/null
+++ b/File Management/Get-DuplicateFile.ps1
@@ -0,0 +1,34 @@
+function Get-DuplicateFile {
+ [CmdletBinding(SupportsShouldProcess = $true)]
+ Param (
+ [Parameter(
+ Mandatory = $false,
+ ValueFromPipeline = $true,
+ ValueFromPipelineByPropertyName = $true
+ )]
+ [ValidateScript({
+ if (Test-Path -Path $_ -PathType Container) {
+ $true
+ } else {
+ throw "[$_] is not a valid directory."
+ $false
+ }
+ })]
+ [string[]]$Directory = '.'
+ )
+
+ begin {
+ $AllItems = @()
+ }
+
+ process {
+ foreach ($Dir in $Directory) {
+ Write-Verbose "Getting all files within [$Dir]..."
+ $AllItems += Get-ChildItem -Path $Dir -File -Recurse -Force -ErrorAction Continue
+ }
+
+ $AllItems | Group-Object -Property Name | Where-Object { $_.Count -gt 1 } | ForEach-Object {
+ $_.Group | ForEach-Object { $_ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/File Management/Get-FilesModifyDate.ps1 b/File Management/Get-FilesModifyDate.ps1
new file mode 100644
index 0000000..292ecad
--- /dev/null
+++ b/File Management/Get-FilesModifyDate.ps1
@@ -0,0 +1,19 @@
+# Get the path this script is running from
+$ScriptPath = Split-Path $MyInvocation.MyCommand.Path -Parent
+
+# Array to store our info in
+$Output = @()
+
+# Servers we're checking
+$Servers = @('server01', 'server02')
+
+# Path we're checking
+$Path = 'ShareName'
+
+# Loop through each server
+ForEach ($Server in $Servers) {
+ $Output += Get-ChildItem "\\$Server\$Path" -Recurse | Where { $_.Extension -eq '.bak' }
+}
+
+# Create csv at script root with output
+$Output | select FullName, LastWriteTime | Export-Csv "$ScriptPath\Backups.csv" -NoTypeInformation
\ No newline at end of file
diff --git a/File Management/Move-EmptyFolder.ps1 b/File Management/Move-EmptyFolder.ps1
new file mode 100644
index 0000000..2f86881
--- /dev/null
+++ b/File Management/Move-EmptyFolder.ps1
@@ -0,0 +1,10 @@
+$Folders = Get-ChildItem '\\server\users'
+
+$EmptyDir = New-Item -ItemType Directory '.\empty' -Force
+
+foreach ($Folder in $Folders) {
+ if ((Get-ChildItem $Folder.FullName).Count -eq 0) {
+ Write-Host "moving: $Folder"
+ Move-Item $Folder.FullName $EmptyDir
+ }
+}
\ No newline at end of file
diff --git a/File Management/Move-GFRClientFiles.ps1 b/File Management/Move-GFRClientFiles.ps1
new file mode 100644
index 0000000..1df3e0c
--- /dev/null
+++ b/File Management/Move-GFRClientFiles.ps1
@@ -0,0 +1,91 @@
+[CmdletBinding(SupportsShouldProcess = $true)]
+param(
+ [Parameter(Mandatory = $true)]
+ [ValidateScript({
+ # Validate the path
+ if (Test-Path -Path $_) {
+ # Validate the directory contains files
+ if ((Get-ChildItem -Path $_ -File).Count) {
+ $true
+ } else {
+ throw "Docs directory is empty"
+ }
+ } else {
+ throw "Invalid path given: $_"
+ }
+ })]
+ [string] $DocsPath,
+
+ [Parameter(Mandatory = $true)]
+ [ValidateScript({
+ # Validate the path
+ if (Test-Path -Path $_) {
+ # Validate the file has the .xml extension
+ if ((Get-Item -Path $_).Extension -eq '.xml') {
+ $true
+ } else {
+ throw "HitList given does not have the '.xml' extension"
+ }
+ } else {
+ throw "Invalid path given: $_"
+ }
+ })]
+ [string] $HitListPath,
+
+ [Parameter(Mandatory = $false)]
+ [switch] $Copy
+)
+
+begin {
+ Write-Verbose "Gathering files within $DocsPath..."
+ $Docs = Get-ChildItem -Path $DocsPath -File
+ $DocsCount = $Docs.Count
+ Write-Verbose "$DocsCount files found."
+
+ Write-Verbose "Importing HitList from $HitListPath (this may take a while depending on the size of the file)..."
+ $HitList = [xml] (Get-Content -Path $HitListPath)
+ $HitListCount = $HitList.dcs.dc.Count
+ Write-Verbose "$HitListCount entries found."
+
+ $HitListDictionary = @{}
+}
+
+process {
+ $Counter = 1
+ foreach ($Doc in $HitList.dcs.dc) {
+ $ClientName = $Doc.i1.'#cdata-section'
+ $FileName = "$($Doc.doc_name).$($Doc.tp)"
+
+ Write-Progress -Activity "Processing HitList..." -Status "($Counter / $HitListCount)" `
+ -PercentComplete ($Counter / $HitListCount * 100) -CurrentOperation "$ClientName - $FileName"
+
+ $HitListDictionary.Add($FileName, $ClientName)
+ $Counter++
+ }
+
+ if ($Copy) { Write-Verbose "Script run in 'Copy' mode - source files will remain intact." }
+
+ $Counter = 1
+ foreach ($File in $Docs) {
+ try {
+ $Client = $HitListDictionary.($File.Name)
+ $ClientDirectory = "$DocsPath\$Client"
+
+ Write-Progress -Activity "Processing Docs..." -Status "($Counter / $DocsCount)" `
+ -PercentComplete ($Counter / $DocsCount * 100) -CurrentOperation $File.FullName
+
+ if (-not (Test-Path -Path $ClientDirectory)) {
+ New-Item -Path $ClientDirectory -ItemType Directory
+ }
+ if ($Copy) {
+ Copy-Item -Path $File.FullName -Destination $ClientDirectory
+ } else {
+ Move-Item -Path $File.FullName -Destination $ClientDirectory
+ }
+ } catch {
+ Write-Error -Message "Error processing $($File.FullName): $($_.Exception.Message)"
+ } finally {
+ $Counter++
+ }
+ }
+}
\ No newline at end of file
diff --git a/File Management/Rename-Season.ps1 b/File Management/Rename-Season.ps1
new file mode 100644
index 0000000..9fba26c
--- /dev/null
+++ b/File Management/Rename-Season.ps1
@@ -0,0 +1,13 @@
+[CmdletBinding()]
+Param(
+ [ValidateScript({ Test-Path -Path $_ -PathType Container })]
+ [string]$Path
+)
+
+Get-ChildItem -Path $Path | ForEach-Object {
+ $NewName = $_.Name -replace 'Season ', 'S' -replace ', Episode ', 'E'
+ $Season = 'S' + "{0:D2}" -f [int]($NewName -split 'S', 2 -split 'E', 2)[1]
+ $Episode = 'E' + "{0:D2}" -f [int]($NewName -split 'E', 2 -split ' ', 2)[1]
+ $NewName = "$Season$Episode - $(($NewName -split ' ', 2)[1])"
+ Rename-Item -Path $_.FullName -NewName $NewName
+}
\ No newline at end of file
diff --git a/Get-BitcoinPrice.ps1 b/Fun/Get-BitcoinPriceChart.ps1
similarity index 88%
rename from Get-BitcoinPrice.ps1
rename to Fun/Get-BitcoinPriceChart.ps1
index 9c02b8e..a6f7f2e 100644
--- a/Get-BitcoinPrice.ps1
+++ b/Fun/Get-BitcoinPriceChart.ps1
@@ -1,6 +1,6 @@
$PriceData = [System.Collections.Specialized.OrderedDictionary]@{}
-$Data = (Invoke-RestMethod -Method Get -Uri 'https://api.coindesk.com/v1/bpi/historical/close.json' `
+$Data = (Invoke-RestMethod -Method Get -Uri 'http://api.coindesk.com/v1/bpi/historical/close.json' `
-ContentType 'application/json').bpi | Out-String
$Data.Trim() -split "`r`n" | ForEach-Object {
@@ -38,10 +38,10 @@ $Chart.Series['Price'].ChartArea = "ChartArea1"
$Chart.Series['Price'].Color = "#62B5CC"
$Chart.Series['Price'].Points.DataBindXY($PriceData.Keys, $PriceData.Values)
-# Display the chart on a form
+# Display the chart on a form
$Chart.Anchor = [System.Windows.Forms.AnchorStyles]::Bottom -bor
- [System.Windows.Forms.AnchorStyles]::Right -bor
- [System.Windows.Forms.AnchorStyles]::Top -bor
+ [System.Windows.Forms.AnchorStyles]::Right -bor
+ [System.Windows.Forms.AnchorStyles]::Top -bor
[System.Windows.Forms.AnchorStyles]::Left
$Form = New-Object Windows.Forms.Form
$Form.Text = "Bitcoin Price Chart"
diff --git a/Fun/Get-FeaturedBrew.ps1 b/Fun/Get-FeaturedBrew.ps1
new file mode 100644
index 0000000..41c04b5
--- /dev/null
+++ b/Fun/Get-FeaturedBrew.ps1
@@ -0,0 +1,14 @@
+# http://www.brewerydb.com/developers/docs
+$BreweryDB = 'http://api.brewerydb.com/v2'
+$APIKey = 'cd4f34c5b35a1a2c4c76dcad8c5253bc'
+$Request = 'features'
+
+$Result = Invoke-RestMethod -Method Get -Uri "$BreweryDB/$Request/?key=$APIKey" -ContentType 'application/json'
+$Result.data | Select-Object Brewery, Beer | ForEach-Object {
+ New-Object -TypeName psobject -Property @{
+ BreweryName = $_.Brewery.Name
+ #BreweryDescription = $_.Brewery.Description
+ BeerName = $_.Beer.Name
+ #BeerDescription = $_.Beer.Description
+ }
+} | Out-GridView
diff --git a/Fun/Get-Weather.ps1 b/Fun/Get-Weather.ps1
new file mode 100644
index 0000000..72a5a31
--- /dev/null
+++ b/Fun/Get-Weather.ps1
@@ -0,0 +1,139 @@
+#Requires -Version 3
+
+$UtilityName = 'Archaic Weather Gathering Utility - Nick Rodriguez'
+
+# Default values
+$City = 'Asheville'
+$Country = 'United States'
+$ZipCode = '28803'
+$Days = '6'
+
+function Get-Weather {
+ param(
+ [Parameter(Mandatory = $true)]
+ [string]$City,
+
+ [Parameter(Mandatory = $true)]
+ [string]$Country
+ )
+
+ $WebService = New-WebServiceProxy -Uri 'http://www.webservicex.net/globalweather.asmx?WSDL'
+ return ([xml]$WebService.GetWeather($City, $Country)).CurrentWeather
+}
+
+function Get-Forecast {
+ param(
+ [Parameter(Mandatory = $true)]
+ [string]$ZipCode,
+
+ [Parameter(Mandatory = $true)]
+ [int]$Days
+ )
+
+ $URI = 'http://www.weather.gov/forecasts/xml/DWMLgen/wsdl/ndfdXML.wsdl'
+ $Proxy = New-WebServiceProxy -uri $URI -namespace WebServiceProxy
+ $LatLonList = ([xml]$Proxy.LatLonListZipCode($ZipCode)).dwml.LatLonlist -split ','
+ $Lat = $LatLonList[0]
+ $Lon = $LatLonList[1]
+ $Date = Get-Date -UFormat %Y-%m-%d
+ $Format = "Item24hourly"
+ [xml]$Weather = $Proxy.NDFDgenByDay($Lat, $Lon, $Date, $Days, 'e', $Format)
+
+
+ $Forecast = for ($Day = 0; $Day -le $Days - 1; $Day ++) {
+ New-Object PSObject -Property @{
+ Date = ((Get-Date).AddDays($i)).ToString("MM/dd/yyyy") ;
+ MaxTemp = $Weather.dwml.data.parameters.temperature[0].Value[$Day] ;
+ MinTemp = $Weather.dwml.data.parameters.temperature[1].Value[$Day] ;
+ Summary = $Weather.dwml.data.parameters.weather."weather-conditions"[$Day]."Weather-summary"
+ }
+ }
+
+ return $Forecast | Format-Table -Property Date, MaxTemp, MinTemp, Summary -AutoSize
+}
+
+function Load-MenuSystem {
+ [int]$MenuLevel1 = 0
+ [int]$MenuLevel2 = 0
+ [boolean]$xValidSelection = $false
+
+ while ($MenuLevel1 -lt 1 -or $MenuLevel1 -gt 3) {
+ Clear-Host
+
+ # Present the Menu Options
+ Write-Host "`n`t$UtilityName`n" -ForegroundColor Magenta
+ Write-Host "`t`tPlease select an option`n" -ForegroundColor Cyan
+ Write-Host "`t`t`t1. Current Weather" -ForegroundColor Cyan
+ Write-Host "`t`t`t2. Forecast" -ForegroundColor Cyan
+ Write-Host "`t`t`t3. Exit`n" -ForegroundColor Cyan
+ # Retrieve the response from the user
+ [int]$MenuLevel1 = Read-Host "`t`tEnter Menu Option Number"
+ if ( $MenuLevel1 -lt 1 -or $MenuLevel1 -gt 3 ) {
+ Write-Host "`tPlease select one of the options available.`n" -ForegroundColor Red
+ Start-Sleep -Seconds 1
+ }
+ }
+
+ switch ($MenuLevel1){ # User has selected a valid entry.. load next menu
+ 1 {
+ while ($MenuLevel2 -lt 1 -or $MenuLevel2 -gt 4) {
+ Clear-Host
+
+ # Present the Menu Options
+ Write-Host "`n`t$UtilityName`n" -ForegroundColor Magenta
+ Write-Host "`t`tCurrent weather for $City, $Country`n" -ForegroundColor Cyan
+ Write-Host "`t`t`t1. Refresh" -ForegroundColor Cyan
+ Write-Host "`t`t`t2. Change City" -ForegroundColor Cyan
+ Write-Host "`t`t`t3. Change Country" -ForegroundColor Cyan
+ Write-Host "`t`t`t4. Go to Main Menu`n" -ForegroundColor Cyan
+ [int]$MenuLevel2 = Read-Host "`t`tEnter Menu Option Number"
+ if( $MenuLevel2 -lt 1 -or $MenuLevel2 -gt 4 ) {
+ Write-Host "`tPlease select one of the options available.`n" -ForegroundColor Red
+ Start-Sleep -Seconds 1
+ }
+ }
+ switch ($MenuLevel2) {
+ 1 {
+ Get-Weather $City $Country
+ pause
+ }
+ 2 { $City = Read-Host "`n`tEnter a city name" }
+ 3 { $Country = Read-Host "`n`tEnter a country name" }
+ default { break }
+ }
+ }
+
+ 2 {
+ while ($MenuLevel2 -lt 1 -or $MenuLevel2 -gt 4) {
+ Clear-Host
+
+ # Present the Menu Options
+ Write-Host "`n`t$UtilityName`n" -Fore Magenta
+ Write-Host "`t`t$Days day forecast for area code $ZipCode`n" -Fore Cyan
+ Write-Host "`t`t`t1. Refresh" -Fore Cyan
+ Write-Host "`t`t`t2. Change Zip Code" -Fore Cyan
+ Write-Host "`t`t`t3. Change Number of Days" -Fore Cyan
+ Write-Host "`t`t`t4. Go to Main Menu`n" -Fore Cyan
+ [int]$MenuLevel2 = Read-Host "`t`tEnter Menu Option Number"
+ }
+ if( $MenuLevel2 -lt 1 -or $MenuLevel2 -gt 4 ){
+ Write-Host "`tPlease select one of the options available.`n" -Fore Red; Start-Sleep -Seconds 1
+ }
+ Switch ($MenuLevel2) {
+ 1 {
+ Get-Forecast $ZipCode $Days
+ pause
+ }
+ 2 { $ZipCode = Read-Host "`n`tEnter a zip code" }
+ 3 { $Days = Read-Host "`n`tEnter the number of days to forecast" }
+ default { break }
+ }
+ }
+
+ default { exit }
+ }
+
+ Load-MenuSystem
+}
+
+Load-MenuSystem
\ No newline at end of file
diff --git a/Fun/Get-XMas.ps1 b/Fun/Get-XMas.ps1
new file mode 100644
index 0000000..5301113
--- /dev/null
+++ b/Fun/Get-XMas.ps1
@@ -0,0 +1,69 @@
+# inspired by:
+# http://forums.microsoft.com/TechNet-FR/ShowPost.aspx?PostID=2555221&SiteID=45
+$notes = @'
+ 4A4 4A4 2A4 4A4 4A4 2A4 4A4 4C4 4F3 8G3 1A4
+ 4Bb4 4Bb4 4Bb4 8Bb4 4Bb4 4A4 4A4 8A4 8A4 4A4 4G3 4G3 4A4 2G3 2C4
+ 4A4 4A4 2A4 4A4 4A4 2A4 4A4 4C4 4F3 4G3 1A4 4Bb4 4Bb4 4Bb4 4Bb4
+ 4Bb4 4A4 4A4 8A4 8A4 4C4 4C4 4Bb4 4G3 1F3 4C3 4A4 4G3 4F3 2C3 8C3 8C3
+ 4C3 4A4 4G3 4F3 1D3 4D3 4Bb4 4A4 4G3 1E3 4C4 4C4 4Bb4 4G3
+ 1A4 4C3 4A4 4G3 4F3 1C3 4C3 4A4 4G3 4F3 1D3
+ 4D3 4Bb3 4A4 4G3 4C4 4C4 4C4 8C4 8C4 4D4 4C4 4Bb4 4G3 4F3 2C4 4A4 4A4 2A4
+ 4A4 4A4 2A4 4A4 4C4 4C3 8G3 1A4 4Bb4 4Bb4 4Bb4 8Bb4 4Bb4 4A4 4A4 8A4 8A4
+ 4A4 4G3 4G3 4A4 2G3 2C4 4A4 4A4 2A4 4A4 4A4 2A4 4A4 4C4 4F3 8G3
+ 1A4 4Bb4 4Bb4 4Bb4 4Bb4 4Bb4 4A4 4A4 8A4 8A4 4C4 4C4 4Bb4 4G3 1F3
+'@
+
+# Note is given by fn=f0 * (a)^n
+# a is the twelth root of 2
+# n is the number of half steps from f0, positive or negative
+# f0 used here is A4 at 440 Hz
+
+$StandardDuration = 1000
+$f0 = 440
+$a = [math]::pow(2,(1/12)) # Twelth root of 2
+
+function Get-Frequency([string]$note)
+{
+ # n is the number of half steps from the fixed note
+ $null = $note -match '([A-G#]{1,2})(\d+)'
+ $octave = ([int] $matches[2]) - 4;
+ $n = $octave * 12 + ( Get-HalfSteps $matches[1] );
+ $f0 * [math]::Pow($a, $n);
+}
+
+function Get-HalfSteps([string]$note)
+{
+ switch($note)
+ {
+ 'A' { 0 }
+ 'A#' { 1 }
+ 'Bb' { 1 }
+ 'B' { 2 }
+ 'C' { 3 }
+ 'C#' { 4 }
+ 'Db' { 4 }
+ 'D' { 5 }
+ 'D#' { 6 }
+ 'Eb' { 6 }
+ 'E' { 7 }
+ 'F' { 8 }
+ 'F#' { 9 }
+ 'Gb' { 9 }
+ 'G' { 10 }
+ 'G#' { 11 }
+ 'Ab' { 11 }
+ }
+}
+
+$notes.Split(' ') | ForEach-Object {
+
+ if ($_ -match '(\d)(.+)')
+ {
+ $duration = $StandardDuration / ([int]$matches[1])
+ $playNote = $matches[2]
+ $freq = Get-Frequency $playNote
+
+ [console]::Beep( $freq, $duration)
+ Start-Sleep -Milliseconds 50
+ }
+}
\ No newline at end of file
diff --git a/Fun/Get-nVidiaDriver.ps1 b/Fun/Get-nVidiaDriver.ps1
new file mode 100644
index 0000000..e8149b3
--- /dev/null
+++ b/Fun/Get-nVidiaDriver.ps1
@@ -0,0 +1,140 @@
+# Most logic taken from https://github.com/ElPumpo/TinyNvidiaUpdateChecker/blob/master/TinyNvidiaUpdateChecker/MainConsole.cs
+
+$osVersion = [Environment]::OSVersion.Version.ToString()
+$is64Bit = [Environment]::Is64BitOperatingSystem
+
+switch ($osVersion) {
+ { $_ -like '10.0*' } {
+ $winVer = '10'
+
+ if ($is64Bit) {
+ $osId = 57
+ }
+ else {
+ $osId = 56
+ }
+ }
+
+ { $_ -like '6.3*' } {
+ $winVer = '8.1'
+
+ if ($is64Bit) {
+ $osId = 41
+ }
+ else {
+ $osId = 40
+ }
+ }
+
+ { $_ -like '6.2*' } {
+ $winVer = '8'
+
+ if ($is64Bit) {
+ $osId = 28
+ }
+ else {
+ $osId = 27
+ }
+ }
+
+ { $_ -like '6.1*' } {
+ $winVer = '7'
+
+ if ($is64Bit) {
+ $osId = 19
+ }
+ else {
+ $osId = 18
+ }
+ }
+}
+
+Write-Verbose "Windows $winVer version $osVersion"
+Write-Verbose "64-Bit: $is64Bit"
+
+$langId = switch (Get-Culture) {
+ 'en-US' { 1 }
+ 'en-GB' { 2 }
+ 'zh-CHS' { 5 }
+ 'zh-CHT' { 6 }
+ 'ja-JP' { 7 }
+ 'ko-KR' { 8 }
+ 'de-DE' { 9 }
+ 'es-ES' { 10 }
+ 'fr-FR' { 12 }
+ 'it-IT' { 13 }
+ 'pl-PL' { 14 }
+ 'pt-BR' { 15 }
+ 'ru-RU' { 16 }
+ 'tr-TR' { 19 }
+ default { 17 }
+}
+
+Write-Verbose "Language ID: $langId"
+
+foreach ($gpu in Get-CimInstance -ClassName Win32_VideoController) {
+ Write-Verbose $gpu.Description
+
+ if ($gpu.Description.Split() -contains 'nvidia') {
+ $gpuName = $gpu.Description.Trim()
+ $offlineGpuVersion = $gpu.DriverVersion
+ }
+ elseif ($gpu.PNPDeviceID.Split() -contains 'ven_10de') {
+ Get-CimInstance -ClassName Win32_SystemEnclosure | ForEach-Object {
+ if ($_.ChassisTypes -eq 3) {
+ $gpuName = "GTX"
+ }
+ else {
+ $gpuName = "GTX M"
+ }
+ }
+ }
+}
+
+if (-not $gpuName) {
+ Write-Warning "No nVidia GPU found"
+ exit
+}
+
+if ($gpuName -contains 'M') {
+ $psId = 99
+ $pfId = 758
+}
+else {
+ $psId = 98
+ $pfId = 756
+}
+
+$gpuUrl = "http://www.nvidia.com/Download/processDriver.aspx?psid=$psID&pfid=$pfID&rpf=1&osid=$osId&lid=$langID&ctk=0"
+$processUrl = Invoke-WebRequest $gpuUrl | Select-Object -ExpandProperty Content
+
+$objXmlHttp = New-Object -ComObject MSXML2.ServerXMLHTTP
+$objXmlHttp.Open("GET", $processUrl, $False)
+$objXmlHttp.Send()
+$response = $objXmlHttp.responseText
+$html = New-Object -Com "HTMLFile"
+$html.IHTMLDocument2_write($response)
+
+$version = $html.getElementById("tdVersion").innerText.Split(' ')[0]
+$releaseDate = $html.getElementById("tdReleaseDate").innerText.Split(' ')[0].Split('.')
+$friendlyReleaseDate = Get-Date -Year $releaseDate[0] -Month $releaseDate[1] -Day $releaseDate[2] -Format D
+$releaseNotes = $html.getElementsByTagName('a') | Where-Object href -like "*release-notes.pdf*" | Select-Object -ExpandProperty href
+$releaseDescription = $html.getElementById("tab1_content").innerText
+$confirmUrl = $html.getElementsByTagName('a') | Where-Object href -like "*/content/DriverDownload-March2009/*" | ForEach-Object {
+ 'http://www.nvidia.com/' + $_.pathname + $_.search
+}
+
+$objXmlHttp.Open("GET", $confirmUrl, $False)
+$objXmlHttp.Send()
+$response = $objXmlHttp.responseText
+$html = New-Object -Com "HTMLFile"
+$html.IHTMLDocument2_write($response)
+
+$downloadUrl = $html.getElementsByTagName('a') | Where-Object href -like "*download.nvidia*" | Select-Object -ExpandProperty href
+
+
+
+Write-Output "Download URL: $downloadUrl"
+Write-Output "Release notes: $releaseNotes"
+Write-Output "Offline version: $offlineGpuVersion"
+Write-Output "Online version: $version released on $friendlyReleaseDate"
diff --git a/Fun/PlayingCards.ps1 b/Fun/PlayingCards.ps1
new file mode 100644
index 0000000..2206d98
--- /dev/null
+++ b/Fun/PlayingCards.ps1
@@ -0,0 +1,201 @@
+#requires -Version 5.0
+
+class Card {
+ [CardCollection]$CardCollection
+ [CardSuit]$CardSuit
+ [CardRank]$CardRank
+ [string]$CardName
+ [string]$CardImage
+
+ # Constructor
+ Card() {
+ $this.CardSuit = [CardSuit]([Enum]::GetValues([CardSuit]) | Get-Random)
+ $this.CardRank = [CardRank]([Enum]::GetValues([CardRank]) | Get-Random)
+ $this.CardName = $this.PrintName()
+ $this.CardImage = $this.PrintImage()
+ }
+
+ # Constructor
+ Card([CardSuit]$CardSuit, [CardRank]$CardRank) {
+ [CardSuit]$this.CardSuit = $CardSuit
+ [CardRank]$this.CardRank = $CardRank
+ $this.CardName = $this.PrintName()
+ $this.CardImage = $this.PrintImage()
+ }
+
+ # Constructor
+ Card([CardCollection]$CardCollection, [CardSuit]$CardSuit, [CardRank]$CardRank) {
+ [CardCollection]$this.CardCollection = $CardCollection
+ [CardSuit]$this.CardSuit = $CardSuit
+ [CardRank]$this.CardRank = $CardRank
+ $this.CardName = $this.PrintName()
+ $this.CardImage = $this.PrintImage()
+ }
+
+ hidden [string] PrintName() {
+ return "$($this.CardRank) of $($this.CardSuit)s"
+ }
+
+ hidden [string] PrintImage() {
+ $Rank = switch ($this.CardRank) {
+ Joker { 'j' }
+ Ace { 'A' }
+ Two { '2' }
+ Three { '3' }
+ Four { '4' }
+ Five { '5' }
+ Six { '6' }
+ Seven { '7' }
+ Eight { '8' }
+ Nine { '9' }
+ Ten { '10' }
+ Jack { 'J' }
+ Queen { 'Q' }
+ King { 'K' }
+ }
+
+ $Suit = switch ($this.CardSuit) {
+ Spade { 'â™ ' }
+ Club { '♣' }
+ Heart { '♥' }
+ Diamond { '♦' }
+ }
+
+ return "$Rank$Suit"
+ }
+}
+
+class CardCollection {
+ [string]$Name = 'Deck'
+ [System.Collections.ArrayList]$Cards = (New-Object System.Collections.ArrayList)
+
+ CardCollection() { }
+
+ CardCollection([string]$Name) {
+ [string]$this.Name = $Name
+ }
+
+ CardCollection([string]$Name, [bool]$Jokers) {
+ [string]$this.Name = $Name
+ $this.NewStandardDeck($Jokers)
+ }
+
+ CardCollection([bool]$Jokers) {
+ $this.NewStandardDeck($Jokers)
+ }
+
+ CardCollection([string]$Name, [int]$DeckCount) {
+ [string]$this.Name = $Name
+ for ($i = 0; $i -lt $DeckCount; $i++) {
+ $this.NewStandardDeck($false)
+ }
+ }
+
+ CardCollection([string]$Name, [int]$DeckCount, [bool]$Jokers) {
+ [string]$this.Name = $Name
+ for ($i = 0; $i -lt $DeckCount; $i++) {
+ $this.NewStandardDeck($Jokers)
+ }
+ }
+
+ CardCollection([int]$DeckCount, [bool]$Jokers) {
+ for ($i = 0; $i -lt $DeckCount; $i++) {
+ $this.NewStandardDeck($Jokers)
+ }
+ }
+
+ NewStandardDeck([bool]$Jokers) {
+ foreach ($CardSuit in [enum]::GetValues([CardSuit])) {
+ foreach ($CardRank in [enum]::GetValues([CardRank])) {
+ if ($Jokers -or [int]$CardRank -gt 0) {
+ $this.Push([Card]::new($this, $CardSuit, $CardRank))
+ }
+ }
+ }
+
+ $this.Shuffle()
+ }
+
+ [void] Clear() {
+ $this.Cards = New-Object System.Collections.ArrayList
+ }
+
+ [void] Push([Card]$Card) {
+ Write-Verbose "Adding $($Card.PrintImage()) to $($this.Name)."
+ $Card.CardCollection = $this
+ $this.Cards.Add($Card)
+ }
+
+ [Card] Pop() {
+ $Card = $this.Cards[-1]
+ Write-Verbose "Removing $($Card.PrintImage()) from $($this.Name)."
+ $Card.CardCollection = $null
+ $this.Cards.Remove($Card)
+ return $Card
+ }
+
+ [void] Shuffle() {
+ $this.Cards = $this.Cards | Sort-Object { Get-Random }
+ }
+
+ [string[]] PrintCards() {
+ return ($this.Cards.GetEnumerator() | ForEach-Object { $_.PrintImage() }) -join ', '
+ }
+}
+
+class Player {
+ [CardCollection]$Hand = [CardCollection]::new()
+ [bool]$Active
+ [int]$Player
+
+ Player([int]$Player) {
+ [int]$this.Player = $Player
+ }
+
+ [void] DrawHand([CardCollection]$Deck, [int]$HandMax) {
+ while ($this.Hand.Cards.Count -lt $HandMax) {
+ $this.Hand.Push($Deck.Pop())
+ }
+ }
+}
+
+class Game {
+ [int]$ActivePlayer
+ [CardCollection]$Deck
+ [Player[]]$Players
+ [int]$HandSize
+
+ Game([int]$PlayerCount, [int]$HandSize) {
+ $this.Deck = [CardCollection]::new($false)
+
+ for ($i = 0; $i -lt $PlayerCount; $i++) {
+ $this.Players += [Player]::new($i)
+ }
+
+ $this.Players.DrawHand($this.Deck, $HandSize)
+ }
+}
+
+enum CardSuit {
+ Spade = 0
+ Club = 1
+ Heart = 2
+ Diamond = 3
+}
+
+enum CardRank {
+ Joker = 0
+ Ace = 1
+ Two = 2
+ Three = 3
+ Four = 4
+ Five = 5
+ Six = 6
+ Seven = 7
+ Eight = 8
+ Nine = 9
+ Ten = 10
+ Jack = 11
+ Queen = 12
+ King = 13
+}
\ No newline at end of file
diff --git a/Send-CatFactMessage.ps1 b/Fun/Send-CatFactMessage.ps1
old mode 100755
new mode 100644
similarity index 63%
rename from Send-CatFactMessage.ps1
rename to Fun/Send-CatFactMessage.ps1
index 59406ac..88ef858
--- a/Send-CatFactMessage.ps1
+++ b/Fun/Send-CatFactMessage.ps1
@@ -1,34 +1,45 @@
-function Send-CatFactMessage {
- <#
- .SYNOPSIS
- Send a cat fact to users on a computer.
- .DESCRIPTION
- Send a random cat fact to any number of computers and all users or a specific user. Supports credential passing.
- .EXAMPLE
- Send-CatFactMessage -PlayAudio
- Sends cat fact message to all users on localhost and outputs fact through speakers.
- .EXAMPLE
- Get-ADComputer -Filter * | Send-CatFactMessage -UserName JDoe -Credential (Get-Credential)
- Send cat fact to jDoe on all AD computers. Prompt user for credentials to run command with.
- .EXAMPLE
- Send-CatFactMessage -ComputerName pc1, pc2, pc3
- Send cat fact to all users on provided computer names.
- .PARAMETER ComputerName
- The computer name to execute against. Default is local computer.
- .PARAMETER UserName
- The name the user to display the message to. Default is all users.
- .PARAMETER PlayAudio
- Use Windows Speech Synthesizer to output the fact using text to speech.
- .PARAMETER Credential
- The credential object to execute the command with.
- #>
-
+function Send-CatFactMessage {
+ <#
+ .SYNOPSIS
+ Send a cat fact to users on a computer.
+
+ .DESCRIPTION
+ Send a random cat fact to any number of computers and all users or a specific user. Supports credential passing.
+
+ .EXAMPLE
+ Send-CatFactMessage -PlayAudio
+ Sends cat fact message to all users on localhost and outputs fact through speakers.
+
+ .EXAMPLE
+ Get-ADComputer -Filter * | Send-CatFactMessage -UserName JDoe -Credential (Get-Credential)
+ Send cat fact to jDoe on all AD computers. Prompt user for credentials to run command with.
+
+ .EXAMPLE
+ Send-CatFactMessage -ComputerName pc1, pc2, pc3
+ Send cat fact to all users on provided computer names.
+
+ .PARAMETER ComputerName
+ The computer name to execute against. Default is local computer.
+
+ .PARAMETER UserName
+ The name the user to display the message to. Default is all users.
+
+ .PARAMETER PlayAudio
+ Use Windows Speech Synthesizer to output the fact using text to speech.
+
+ .PARAMETER DisplaySeconds
+ Seconds to display the message on the user's screen. Default is 0, which waits for the user to click a button.
+
+ .PARAMETER Credential
+ The credential object to execute the command with.
+ #>
+
[CmdletBinding(SupportsShouldProcess = $true)]
param (
- [Parameter(
- Mandatory = $false,
- ValueFromPipeline = $true,
- ValueFromPipelineByPropertyName = $true
+ [Parameter(
+ Mandatory = $false,
+ ValueFromPipeline = $true,
+ ValueFromPipelineByPropertyName = $true
)]
[string[]]$ComputerName = $env:COMPUTERNAME,
@@ -38,42 +49,57 @@
[Parameter(Mandatory = $false)]
[switch]$PlayAudio,
+ [Parameter(Mandatory = $false)]
+ [int]$DisplaySeconds = 0,
+
[Parameter(Mandatory = $false)]
[PSCredential]$Credential
)
-
- $CatFact = (ConvertFrom-Json (Invoke-WebRequest -Uri 'http://catfacts-api.appspot.com/api/facts')).facts
-
- if ($pscmdlet.ShouldProcess("User: $UserName, Computer: $ComputerName", "Send cat fact, $CatFact")) {
- $ScriptBlock = {
- param (
- [string]$UserName,
-
- [string]$CatFact,
-
- [bool]$PlayAudio = $false
- )
-
- msg $UserName $CatFact
-
- if ($PlayAudio) {
- Add-Type -AssemblyName System.Speech
- $SpeechSynth = New-Object System.Speech.Synthesis.SpeechSynthesizer
- $SpeechSynth.Speak($CatFact)
- }
- }
-
- if ($Credential) {
- Write-Verbose "Sending cat fact using credential $($Credential.UserName)"
-
- Invoke-Command -ComputerName $ComputerName -ScriptBlock $ScriptBlock `
- -ArgumentList $UserName, $CatFact, $PlayAudio -AsJob -Credential $Credential
- } else {
- Invoke-Command -ComputerName $ComputerName -ScriptBlock $ScriptBlock `
- -ArgumentList $UserName, $CatFact, $PlayAudio -AsJob
- }
-
- Get-Job | Wait-Job | Receive-Job
- Get-Job | Remove-Job
- }
+
+ [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
+ $CatFact = Invoke-RestMethod -Uri 'https://catfact.ninja/fact' -Method Get |
+ Select-Object -ExpandProperty fact
+
+ if ($pscmdlet.ShouldProcess("User: $UserName, Computer: $ComputerName", "Send cat fact, $CatFact")) {
+ $ScriptBlock = {
+ param (
+ [Parameter(Mandatory = $true)]
+ [string]$UserName,
+
+ [Parameter(Mandatory = $true)]
+ [string]$CatFact,
+
+ [Parameter(Mandatory = $true)]
+ [bool]$PlayAudio = $false,
+
+ [Parameter(Mandatory = $true)]
+ [int]$DisplaySeconds
+ )
+
+ if ($DisplaySeconds -gt 0) {
+ msg $UserName /time:$DisplaySeconds $CatFact
+ } else {
+ msg $UserName $CatFact
+ }
+
+ if ($PlayAudio) {
+ Add-Type -AssemblyName System.Speech
+ $SpeechSynth = New-Object System.Speech.Synthesis.SpeechSynthesizer
+ $SpeechSynth.Speak($CatFact)
+ }
+ }
+
+ $Params = @{
+ 'ComputerName' = $ComputerName
+ 'ScriptBlock' = $ScriptBlock
+ 'ArgumentList' = @($UserName, $CatFact, $PlayAudio, $DisplaySeconds)
+ 'AsJob' = $true
+ }
+ if ($Credential) { $Params.Add('Credential', $Credential) }
+
+ Invoke-Command @Params
+
+ Get-Job | Wait-Job | Receive-Job
+ Get-Job | Remove-Job
+ }
}
\ No newline at end of file
diff --git a/Write-Type.ps1 b/Fun/Write-Type.ps1
similarity index 96%
rename from Write-Type.ps1
rename to Fun/Write-Type.ps1
index 667f84b..509376a 100644
--- a/Write-Type.ps1
+++ b/Fun/Write-Type.ps1
@@ -1,49 +1,49 @@
-function Write-Type {
- <#
- .Synopsis
- Make Write-Host text appear as if it is being typed
-
- .DESCRIPTION
- Input text and if desired specify the write speed (25-500 milliseconds) and foreground color for the text
-
- .EXAMPLE
- Write-Typewriter 'Hello world!'
-
- .EXAMPLE
- Write-Typewriter 'Hello world!' 250
-
- .EXAMPLE
- Write-Typewriter -Text '2 spooky 4 me!' -TypeSpeed 400 -ForegroundColor 'Red'
-
- .NOTES
- v1.1 - 2016-04-04 - Nick Rodriguez
- -Changed name
- -Changed TypeSpeed range
- -Added ForegroundColor param
- -Changed sleep to not use method after seeing it slow performance with Measure-Command
- -Changed code formatting to my liking
-
- v1.0 - 2016-01-25 - Nathan Kasco (http://poshcode.org/6193)
- #>
-
- [CmdletBinding()]
- [OutputType([string])]
-
- param (
- [Parameter(Mandatory = $true, Position = 0)]
- [string] $Text,
-
- [Parameter(Mandatory = $false, Position = 1)]
- [ValidateRange(25, 500)]
- [int] $TypeSpeed = 125,
-
- [Parameter(Mandatory = $false, Position = 2)]
- [string] $ForegroundColor = 'White'
- )
-
- # Pause after typing each letter
- $Text.GetEnumerator() | ForEach-Object {
- Write-Host $_ -NoNewline -ForegroundColor $ForegroundColor
- Start-Sleep -Milliseconds $TypeSpeed
- }
+function Write-Type {
+ <#
+ .Synopsis
+ Make Write-Host text appear as if it is being typed
+
+ .DESCRIPTION
+ Input text and if desired specify the write speed (25-500 milliseconds) and foreground color for the text
+
+ .EXAMPLE
+ Write-Typewriter 'Hello world!'
+
+ .EXAMPLE
+ Write-Typewriter 'Hello world!' 250
+
+ .EXAMPLE
+ Write-Typewriter -Text '2 spooky 4 me!' -TypeSpeed 400 -ForegroundColor 'Red'
+
+ .NOTES
+ v1.1 - 2016-04-04 - Nick Rodriguez
+ -Changed name
+ -Changed TypeSpeed range
+ -Added ForegroundColor param
+ -Changed sleep to not use method after seeing it slow performance with Measure-Command
+ -Changed code formatting to my liking
+
+ v1.0 - 2016-01-25 - Nathan Kasco (http://poshcode.org/6193)
+ #>
+
+ [CmdletBinding()]
+ [OutputType([string])]
+
+ param (
+ [Parameter(Mandatory = $true, Position = 0)]
+ [string] $Text,
+
+ [Parameter(Mandatory = $false, Position = 1)]
+ [ValidateRange(25, 500)]
+ [int] $TypeSpeed = 125,
+
+ [Parameter(Mandatory = $false, Position = 2)]
+ [string] $ForegroundColor = 'White'
+ )
+
+ # Pause after typing each letter
+ $Text.GetEnumerator() | ForEach-Object {
+ Write-Host $_ -NoNewline -ForegroundColor $ForegroundColor
+ Start-Sleep -Milliseconds $TypeSpeed
+ }
}
\ No newline at end of file
diff --git a/GUI/Create-MsgBoxWithImage.ps1 b/GUI/Create-MsgBoxWithImage.ps1
new file mode 100644
index 0000000..bb6f336
--- /dev/null
+++ b/GUI/Create-MsgBoxWithImage.ps1
@@ -0,0 +1,21 @@
+Add-Type -AssemblyName System.Windows.Forms
+
+# Get the Base64 string of an image from here https://www.base64-image.de/ and paste below
+$Base64Icon = ''
+$IconStream = [System.IO.MemoryStream][System.Convert]::FromBase64String($Base64Icon)
+$IconBMP = [System.Drawing.Bitmap][System.Drawing.Image]::FromStream($IconStream)
+
+$form = New-Object System.Windows.Forms.Form
+$form.Text = 'Test Title'
+$form.Size = '640, 480'
+$form.FormBorderStyle='FixedToolWindow'
+$form.StartPosition='CenterScreen'
+
+$image=New-Object Windows.Forms.PictureBox
+$image.Size='256,256'
+$image.Image = $IconBMP
+$image.Location='50,50'
+$form.controls.add($image)
+
+$form.Add_Shown({$form.Activate()})
+[void]$form.ShowDialog()
\ No newline at end of file
diff --git a/GUI/Get-Directory.ps1 b/GUI/Get-Directory.ps1
new file mode 100644
index 0000000..644fb0f
--- /dev/null
+++ b/GUI/Get-Directory.ps1
@@ -0,0 +1,14 @@
+function Get-Directory {
+ [CmdletBinding()]
+ [OutputType([psobject])]
+ param()
+
+ [System.Reflection.Assembly]::LoadWithPartialName("System.windows.forms") | Out-Null
+ $OpenDirectoryDialog = New-Object Windows.Forms.FolderBrowserDialog
+ $OpenDirectoryDialog.ShowDialog() | Out-Null
+ try {
+ Get-Item $OpenDirectoryDialog.SelectedPath
+ } catch {
+ Write-Warning 'Open Directory Dialog was closed or cancelled without selecting a Directory'
+ }
+}
\ No newline at end of file
diff --git a/GUI/Get-File.ps1 b/GUI/Get-File.ps1
new file mode 100644
index 0000000..9d6af55
--- /dev/null
+++ b/GUI/Get-File.ps1
@@ -0,0 +1,112 @@
+Function Get-File {
+ <#
+ .SYNOPSIS
+ Prompt user to select a file.
+
+ .DESCRIPTION
+
+
+ .PARAMETER TypeName
+ The type of file you're prompting for. This appears in the Open File Dialog and is only used to help the user.
+
+ .PARAMETER TypeExtension
+ The extension you're prompting for (e.g. "exe")
+
+ .PARAMETER MultipleExtensions
+ Filter by multiple extensions. Comma separated list.
+
+ .PARAMETER MultipleFiles
+ Use this to allow the user to select multiple files.
+
+ .PARAMETER InitialDirectory
+ Directory the Open File Dialog will start from.
+
+ .PARAMETER Title
+ Title that will appear in the Title Bar of the Open File Dialog.
+
+ .INPUTS
+ None. You cannot pipe input to this function.
+
+ .OUTPUTS
+ System.IO.FileSystemInfo
+
+ .EXAMPLE
+ Get-File
+ # Prompts the user to select a file of any type
+
+ .EXAMPLE
+ Get-File -TypeName 'Setup File' -TypeExtension 'msi' -InitialDirectory 'C:\Temp\Downloads'
+ # Prompts the user to select an msi file and begin the prompt in the C:\Temp\Downloads directory
+
+ .EXAMPLE
+ Get-File -TypeName 'Log File' -MultipleExtensions 'log', 'txt' -MultipleFiles
+ # Prompts the user to select one or more txt or log file
+
+ .NOTES
+ Created by Nick Rodriguez
+
+ Version 1.0 - 2/26/16
+
+ #>
+ [CmdletBinding(DefaultParameterSetName = 'SingleExtension')]
+ [OutputType([psobject[]])]
+ param (
+ [Parameter(Mandatory=$false, ParameterSetName = 'SingleExtension')]
+ [string]
+ $TypeName = 'All Files (*.*)',
+
+ [Parameter(Mandatory=$false, ParameterSetName = 'SingleExtension')]
+ [string]
+ $TypeExtension = '*',
+
+ [Parameter(Mandatory=$false, ParameterSetName = 'MultipleExtensions')]
+ [string[]]
+ $MultipleExtensions,
+
+ [Parameter(Mandatory=$false)]
+ [switch]
+ $MultipleFiles,
+
+ [Parameter(Mandatory=$false)]
+ [ValidateScript({
+ if (-not (Test-Path $_ )) {
+ throw "The path [$_] was not found."
+ } else { $true }
+ })]
+ [string[]]
+ $InitialDirectory = $PSScriptRoot,
+
+ [Parameter(Mandatory=$false)]
+ [string]
+ $Title = 'Select a file'
+ )
+
+ [System.Reflection.Assembly]::LoadWithPartialName("System.windows.forms") | Out-Null
+
+ $OpenFileDialog = New-Object System.Windows.Forms.OpenFileDialog
+ $OpenFileDialog.Title = $Title
+ $OpenFileDialog.InitialDirectory = $InitialDirectory
+
+ if ($PSCmdlet.ParameterSetName -eq 'MultipleExtensions' ) {
+ foreach ($Extension in $MultipleExtensions) {
+ $TypeExtensionName += "*.$Extension, "
+ $TypeExtensionFilter += "*.$Extension; "
+ }
+ $TypeExtensionName = $TypeExtensionName.TrimEnd(', ')
+ $TypeExtension = $TypeExtension.TrimEnd('; ')
+ $OpenFileDialog.Filter = "$TypeName ($TypeExtensionName)| $TypeExtensionFilter"
+ } else {
+ $OpenFileDialog.Filter = "$TypeName (*.$TypeExtension)| *.$TypeExtension"
+ }
+
+ $OpenFileDialog.ShowHelp = $true
+ $OpenFileDialog.ShowDialog() | Out-Null
+
+ try {
+ if ($MultipleFiles) {
+ foreach ($FileName in $OpenFileDialog.FileNames) { Get-Item $FileName }
+ } else {
+ Get-Item $OpenFileDialog.FileName
+ }
+ } catch { } # User closed the window or hit Cancel, return nothing
+}
\ No newline at end of file
diff --git a/GUI/New-MsgBoxWithImage.ps1 b/GUI/New-MsgBoxWithImage.ps1
new file mode 100644
index 0000000..bb6f336
--- /dev/null
+++ b/GUI/New-MsgBoxWithImage.ps1
@@ -0,0 +1,21 @@
+Add-Type -AssemblyName System.Windows.Forms
+
+# Get the Base64 string of an image from here https://www.base64-image.de/ and paste below
+$Base64Icon = ''
+$IconStream = [System.IO.MemoryStream][System.Convert]::FromBase64String($Base64Icon)
+$IconBMP = [System.Drawing.Bitmap][System.Drawing.Image]::FromStream($IconStream)
+
+$form = New-Object System.Windows.Forms.Form
+$form.Text = 'Test Title'
+$form.Size = '640, 480'
+$form.FormBorderStyle='FixedToolWindow'
+$form.StartPosition='CenterScreen'
+
+$image=New-Object Windows.Forms.PictureBox
+$image.Size='256,256'
+$image.Image = $IconBMP
+$image.Location='50,50'
+$form.controls.add($image)
+
+$form.Add_Shown({$form.Activate()})
+[void]$form.ShowDialog()
\ No newline at end of file
diff --git a/GUI/Prompt.ps1 b/GUI/Prompt.ps1
new file mode 100644
index 0000000..8d59dbe
--- /dev/null
+++ b/GUI/Prompt.ps1
@@ -0,0 +1,22 @@
+# Give the user an interface to run options
+Function Prompt {
+ $Title = "Title"
+ $Message = "What do you want to do?"
+ $Get = New-Object System.Management.Automation.Host.ChoiceDescription "&Get", "Description."
+ $Start = New-Object System.Management.Automation.Host.ChoiceDescription "&Start", "Description."
+ $Kill = New-Object System.Management.Automation.Host.ChoiceDescription "&Kill", "Description."
+ $Exit = New-Object System.Management.Automation.Host.ChoiceDescription "&Exit", "Exits this utility."
+ $Options = [System.Management.Automation.Host.ChoiceDescription[]]($Get, $Start, $Kill, $Exit)
+ $Result = $Host.UI.PromptForChoice($Title, $Message, $Options, 0)
+
+ Switch ($Result) {
+ 0 {'Getting services...'; break}
+ 1 {'Starting services...'; break}
+ 2 {'Stopping Services...'; break}
+ 3 {'Exiting...'; exit}
+ }
+
+ Prompt
+}
+
+Prompt
\ No newline at end of file
diff --git a/GUI/Read-FolderBrowserDialog.ps1 b/GUI/Read-FolderBrowserDialog.ps1
new file mode 100644
index 0000000..71f62e8
--- /dev/null
+++ b/GUI/Read-FolderBrowserDialog.ps1
@@ -0,0 +1,5 @@
+function Read-FolderBrowserDialog {
+ $ShellApp = New-Object -ComObject Shell.Application
+ $Directory = $ShellApp.BrowseForFolder(0, 'Select a directory', 0, 'C:\')
+ if ($Directory) { return $Directory.Self.Path } else { return '' }
+}
\ No newline at end of file
diff --git a/Migrate-SQLBackup.ps1 b/GUI/Read-Menu.ps1
similarity index 86%
rename from Migrate-SQLBackup.ps1
rename to GUI/Read-Menu.ps1
index dd70ba4..3104943 100644
--- a/Migrate-SQLBackup.ps1
+++ b/GUI/Read-Menu.ps1
@@ -1,24 +1,24 @@
-$OldSQL = Read-Host 'Current SQL server'
-$NewSQL = Read-Host 'New SQL server'
-$DB = Read-Host 'Name of Database'
-
-# Prompt the user whether they want to merge the database
-Function Prompt {
- CLS
- $Title = "Database Backup Migration`n`n`tOLD SQL SERVER`t:`t$OldSQL`n`tNEW SQL SERVER`t:`t$NewSQL`n`tDATABASE`t:`t$DB"
- $Message = "`nDo you want to merge database?"
- $Yes = New-Object System.Management.Automation.Host.ChoiceDescription "&Yes", "Perform a database merge on $DB, moving it from $OldSQL to $NewSQL."
- $No = New-Object System.Management.Automation.Host.ChoiceDescription "&No", 'Exit this utility.'
- $Options = [System.Management.Automation.Host.ChoiceDescription[]]($Yes, $No)
- $Result = $Host.UI.PromptForChoice($Title, $Message, $Options, 0)
- CLS
-
- switch ($Result) {
- 0 {'Merging...'; Start-Sleep -Seconds 1; Break}
- 1 {'Exiting...'; Start-Sleep -Seconds 1; Exit}
- }
-
- Prompt
-}
-
+$OldSQL = Read-Host 'Current SQL server'
+$NewSQL = Read-Host 'New SQL server'
+$DB = Read-Host 'Name of Database'
+
+# Prompt the user whether they want to merge the database
+Function Prompt {
+ CLS
+ $Title = "Database Backup Migration`n`n`tOLD SQL SERVER`t:`t$OldSQL`n`tNEW SQL SERVER`t:`t$NewSQL`n`tDATABASE`t:`t$DB"
+ $Message = "`nDo you want to merge database?"
+ $Yes = New-Object System.Management.Automation.Host.ChoiceDescription "&Yes", "Perform a database merge on $DB, moving it from $OldSQL to $NewSQL."
+ $No = New-Object System.Management.Automation.Host.ChoiceDescription "&No", 'Exit this utility.'
+ $Options = [System.Management.Automation.Host.ChoiceDescription[]]($Yes, $No)
+ $Result = $Host.UI.PromptForChoice($Title, $Message, $Options, 0)
+ CLS
+
+ switch ($Result) {
+ 0 { 'Merging...'; Start-Sleep -Seconds 1; Break }
+ 1 { 'Exiting...'; Start-Sleep -Seconds 1; Exit }
+ }
+
+ Prompt
+}
+
Prompt
\ No newline at end of file
diff --git a/GUI/Read-YesOrNo.ps1 b/GUI/Read-YesOrNo.ps1
new file mode 100644
index 0000000..99d6e24
--- /dev/null
+++ b/GUI/Read-YesOrNo.ps1
@@ -0,0 +1,7 @@
+function Read-YesOrNo {
+ param ([string] $Message)
+
+ $Prompt = Read-Host $Message
+ while ('yes', 'no' -notcontains $Prompt) { $Prompt = Read-Host "Please enter either 'yes' or 'no'" }
+ if ($Prompt -eq 'yes') { $true } else { $false }
+}
\ No newline at end of file
diff --git a/Get-Certificate.ps1 b/Get-Certificate.ps1
new file mode 100644
index 0000000..2dac1d4
--- /dev/null
+++ b/Get-Certificate.ps1
@@ -0,0 +1,116 @@
+<#
+ .NOTES
+ ===========================================================================
+ Created with: SAPIEN Technologies, Inc., PowerShell Studio 2016 v5.2.126
+ Created on: 7/27/2016 11:37 AM
+ Created by: Nick Rodriguez
+ Filename: Get-Certificate.ps1
+ ===========================================================================
+ .DESCRIPTION
+ Get certificates installed on local or remote computers
+ .PARAMETER Name
+ Computer name or IP to search. Can be an array or a piped variable.
+ .PARAMETER Subject
+ Certificate subject name to search for. By default all subjects will be returned. Wildcards supported (e.g. "*DHG*").
+ .PARAMETER DaysLeft
+ Days left before a certificate expires. To return all certs that expire within 30 days, set this to 30.
+ .PARAMETER NotExpired
+ Use this switch to only return certificates that have not expired.
+#>
+
+function Get-Certificate {
+ [cmdletbinding()]
+ param (
+ [Parameter(
+ ValueFromPipeline = $true,
+ ValueFromPipelineByPropertyName = $true
+ )]
+ [Alias('ComputerName', 'Computer')]
+ [string[]]$Name = 'localhost',
+
+ [PSCredential]$Credential,
+
+ [string]$Subject = '*'
+ )
+
+ DynamicParam {
+ # Set the dynamic parameters' name
+ $ParameterName = 'StorePath'
+
+ # Create the dictionary
+ $RuntimeParameterDictionary = New-Object System.Management.Automation.RuntimeDefinedParameterDictionary
+
+ # Create the collection of attributes
+ $AttributeCollection = New-Object System.Collections.ObjectModel.Collection[System.Attribute]
+
+ # Create and set the parameters' attributes
+ $ParameterAttribute = New-Object System.Management.Automation.ParameterAttribute
+ $ParameterAttribute.Mandatory = $true
+ $ParameterAttribute.Position = 2
+
+ # Add the attributes to the attributes collection
+ $AttributeCollection.Add($ParameterAttribute)
+
+ # Generate and set the ValidateSet
+ $arrSet = Get-ChildItem -Path 'Cert:' | ForEach-Object {
+ $Parent = $_.Location
+ Get-ChildItem -Path "Cert:\$Parent" | ForEach-Object {
+ Write-Output "Cert:\$Parent\$($_.Name)"
+ }
+ }
+
+ $ValidateSetAttribute = New-Object System.Management.Automation.ValidateSetAttribute($arrSet)
+
+ # Add the ValidateSet to the attributes collection
+ $AttributeCollection.Add($ValidateSetAttribute)
+
+ # Create and return the dynamic parameter
+ $RuntimeParameter = New-Object System.Management.Automation.RuntimeDefinedParameter($ParameterName, [string], $AttributeCollection)
+ $RuntimeParameterDictionary.Add($ParameterName, $RuntimeParameter)
+ return $RuntimeParameterDictionary
+ }
+
+ begin {
+ $StorePath = $PsBoundParameters[$ParameterName]
+
+ $ScriptBlock = {
+ [cmdletbinding()]
+ param (
+ [string]$StorePath,
+
+ [string]$Subject = '*'
+ )
+
+ # Get all certs in specified store filtering by subject if provided
+ Get-ChildItem -Path $StorePath | Where-Object {
+ $_.Subject -like $Subject
+ } | ForEach-Object {
+ $ExtensionsFormatted = try { $_.Extensions.Format(1) } catch { }
+ $Template = try { ($_.Extensions.Format(0) -replace "(.+)?=(.+)\((.+)?", '$2')[0] } catch { }
+ $Days = (New-TimeSpan -End $_.NotAfter).Days
+
+ $Cert = $_.psobject.Copy()
+ $Cert | Add-Member -Name ExtensionsFormatted -MemberType NoteProperty -Value $ExtensionsFormatted
+ $Cert | Add-Member -Name DaysUntilExpired -MemberType NoteProperty -Value $Days
+ $Cert | Add-Member -Name Template -MemberType NoteProperty -Value $Template
+
+ Write-Output $Cert
+ }
+ }
+ }
+
+ process {
+ if ($Credential) {
+ Write-Verbose 'Credentials provided'
+ Invoke-Command -ComputerName $Name -ScriptBlock $ScriptBlock -ArgumentList $StorePath, $Subject -Credential $Credential
+ } else {
+ Write-Verbose 'Credentials not provided'
+ Invoke-Command -ComputerName $Name -ScriptBlock $ScriptBlock -ArgumentList $StorePath, $Subject
+ }
+ }
+}
+
+$Computers = @('server01', 'server03')
+
+$Computers | Get-Certificate -Credential $Creds -StorePath Cert:\LocalMachine\AuthRoot |
+ Select-Object FriendlyName, DaysUntilExpired, PSComputerName, Template, ExtensionsFormatted | Format-Table -AutoSize
\ No newline at end of file
diff --git a/Get-WanSpeed.ps1 b/Get-WanSpeed.ps1
new file mode 100644
index 0000000..aeb6085
--- /dev/null
+++ b/Get-WanSpeed.ps1
@@ -0,0 +1,100 @@
+<#PSScriptInfo
+
+.VERSION 2.0
+
+.GUID a6048a09-3e66-467a-acd4-ce3e97098a65
+
+.AUTHOR velecky@velecky.onmicrosoft.com modified by Nick Rodriguez
+
+.PROJECTURI https://www.powershellgallery.com/packages/Speedtest/2.0
+
+.DESCRIPTION
+ WAN speed test
+
+#>
+
+[CmdletBinding()]
+param(
+ [int]$Repetitions = 4
+)
+
+function Invoke-SpeedTest {
+ param(
+ [string]$UploadUrl
+ )
+
+ $topServerUrlSpilt = $UploadUrl -split 'upload'
+ $url = $topServerUrlSpilt[0] + 'random2000x2000.jpg'
+ $col = New-Object System.Collections.Specialized.NameValueCollection
+ $wc = New-Object System.Net.WebClient
+ $wc.QueryString = $col
+ $downloadElaspedTime = (Measure-Command { $webpage1 = $wc.DownloadData($url) }).TotalMilliseconds
+ $downSize = ($webpage1.length + $webpage2.length) / 1MB
+ $downloadSize = [Math]::Round($downSize, 2)
+ $downloadTimeSec = $downloadElaspedTime * 0.001
+ $downSpeed = ($downloadSize / $downloadTimeSec) * 8
+ $downloadSpeed = [Math]::Round($downSpeed, 2)
+
+ Write-Verbose "Downloaded $downloadSize MB in $downloadTimeSec seconds at a speed of $downloadSpeed mbps"
+
+ return $downloadSpeed
+}
+
+# Interact with speedtest page avoiding api
+$objXmlHttp = New-Object -ComObject MSXML2.ServerXMLHTTP
+$objXmlHttp.Open("GET", "http://www.speedtest.net/speedtest-config.php", $False)
+$objXmlHttp.Send()
+[xml]$content = $objXmlHttp.responseText
+
+# Select closest server based on lat/lon
+$oriLat = $content.settings.client.lat
+$oriLon = $content.settings.client.lon
+Write-Verbose "Latitude: $oriLat"
+Write-Verbose "Longitude: $oriLon"
+
+# Make another request, this time to get the server list from the site
+$objXmlHttp.Open("GET", "http://www.speedtest.net/speedtest-servers.php", $False)
+$objXmlHttp.Send()
+[xml]$ServerList = $objXmlHttp.responseText
+
+# Cons contains all of the information about every server in the speedtest.net database
+$cons = $ServerList.settings.servers.server
+
+# Calculate servers relative closeness by doing math against latitude and longitude
+Write-Verbose "Searching for closest geographical servers from list of $($cons.Count)..."
+foreach ($val in $cons) {
+ $R = 6371
+ $pi = [Math]::PI
+
+ [float]$dlat = ([float]$oriLat - [float]$val.lat) * $pi / 180
+ [float]$dlon = ([float]$oriLon - [float]$val.lon) * $pi / 180
+ [float]$a = [math]::Sin([float]$dLat / 2) * [math]::Sin([float]$dLat / 2) + [math]::Cos([float]$oriLat * $pi / 180 ) * [math]::Cos([float]$val.lat * $pi / 180 ) * [math]::Sin([float]$dLon / 2) * [math]::Sin([float]$dLon / 2)
+ [float]$c = 2 * [math]::Atan2([math]::Sqrt([float]$a ), [math]::Sqrt(1 - [float]$a))
+ [float]$d = [float]$R * [float]$c
+
+ $serverInformation += @([pscustomobject]@{
+ Distance = $d
+ Country = $val.country
+ Sponsor = $val.sponsor
+ Url = $val.url
+ })
+}
+
+$serverInformation = $serverInformation | Sort-Object -Property distance
+
+$speedResults = @()
+
+for ($i = 0; $i -lt $Repetitions; $i++) {
+ $url = $serverInformation[$i].url
+ Write-Verbose "Download attempt ($($i + 1) of $Repetitions) from $url..."
+ $speed = Invoke-SpeedTest $url
+ $speedResults += $speed
+}
+
+$results = $speedResults | Measure-Object -Average -Minimum -Maximum
+
+New-Object psobject -Property @{
+ "Fastest (mbps)" = $results.Maximum
+ "Slowest (mbps)" = $results.Minimum
+ "Average (mbps)" = $results.Average
+}
\ No newline at end of file
diff --git a/Get-Weather.ps1 b/Get-Weather.ps1
deleted file mode 100644
index 0faa0a4..0000000
--- a/Get-Weather.ps1
+++ /dev/null
@@ -1,110 +0,0 @@
-$UtilityName = 'Archaic Weather Gathering Utility - Nick Rodriguez'
-
-# Default values
-$City = 'Asheville'
-$Country = 'United States'
-$ZipCode = '28803'
-$Days = '6'
-
-Function Get-Weather {
- param([string]$City, [string]$Country)
- $webservice = New-WebServiceProxy -Uri 'http://www.webservicex.net/globalweather.asmx?WSDL'
- $CurrentWeather = ([xml]$webservice.GetWeather($City, $Country)).CurrentWeather
- return $CurrentWeather
-}
-
-Function Get-Forecast {
- Param([string]$ZipCode, [int]$Days)
- $URI = 'http://www.weather.gov/forecasts/xml/DWMLgen/wsdl/ndfdXML.wsdl'
- $Proxy = New-WebServiceProxy -uri $URI -namespace WebServiceProxy
- [xml]$latlon=$proxy.LatLonListZipCode($ZipCode)
- $Forecast = foreach($l in $latlon) {
- $a = $l.dwml.latlonlist -split ","
- $lat = $a[0]
- $lon = $a[1]
- $sDate = get-date -UFormat %Y-%m-%d
- $format = "Item24hourly"
- [xml]$weather = $Proxy.NDFDgenByDay($lat,$lon,$sDate,$Days,'e',$format)
- For($i = 0 ; $i -le $Days -1 ; $i ++) {
- New-Object psObject -Property @{
- "Date" = ((Get-Date).addDays($i)).tostring("MM/dd/yyyy") ;
- "maxTemp" = $weather.dwml.data.parameters.temperature[0].value[$i] ;
- "minTemp" = $weather.dwml.data.parameters.temperature[1].value[$i] ;
- "Summary" = $weather.dwml.data.parameters.weather."weather-conditions"[$i]."Weather-summary"
- }
- }
- }
-
- return $Forecast | Format-Table -Property date, maxTemp, minTemp, Summary -AutoSize
-}
-
-Function LoadMenuSystem {
- [INT]$MenuLevel1=0
- [INT]$xMenuLevel2=0
- [BOOLEAN]$xValidSelection=$false
- while ( $MenuLevel1 -lt 1 -or $MenuLevel1 -gt 3 ) {
- CLS
- #… Present the Menu Options
- Write-Host "`n`t$UtilityName`n" -ForegroundColor Magenta
- Write-Host "`t`tPlease select an option`n" -Fore Cyan
- Write-Host "`t`t`t1. Current Weather" -Fore Cyan
- Write-Host "`t`t`t2. Forecast" -Fore Cyan
- Write-Host "`t`t`t3. Exit`n" -Fore Cyan
- #… Retrieve the response from the user
- [int]$MenuLevel1 = Read-Host "`t`tEnter Menu Option Number"
- if( $MenuLevel1 -lt 1 -or $MenuLevel1 -gt 3 ) {
- Write-Host "`tPlease select one of the options available.`n" -Fore Red; Start-Sleep -Seconds 1
- }
- }
- Switch ($MenuLevel1){ #… User has selected a valid entry.. load next menu
- 1 {
- while ( $xMenuLevel2 -lt 1 -or $xMenuLevel2 -gt 4 ) {
- CLS
- # Present the Menu Options
- Write-Host "`n`t$UtilityName`n" -Fore Magenta
- Write-Host "`t`tCurrent weather for $City, $Country`n" -Fore Cyan
- Write-Host "`t`t`t1. Refresh" -Fore Cyan
- Write-Host "`t`t`t2. Change City" -Fore Cyan
- Write-Host "`t`t`t3. Change Country" -Fore Cyan
- Write-Host "`t`t`t4. Go to Main Menu`n" -Fore Cyan
- [int]$xMenuLevel2 = Read-Host "`t`tEnter Menu Option Number"
- if( $xMenuLevel2 -lt 1 -or $xMenuLevel2 -gt 4 ) {
- Write-Host "`tPlease select one of the options available.`n" -Fore Red; Start-Sleep -Seconds 1
- }
- }
- Switch ($xMenuLevel2) {
- 1{ Get-Weather $City $Country; pause }
- 2{ $City = Read-Host "`n`tEnter a city name" }
- 3{ $Country = Read-Host "`n`tEnter a country name" }
- default { break}
- }
- }
- 2 {
- while ( $xMenuLevel2 -lt 1 -or $xMenuLevel2 -gt 4 ) {
- CLS
- # Present the Menu Options
- Write-Host "`n`t$UtilityName`n" -Fore Magenta
- Write-Host "`t`t$Days day forecast for area code $ZipCode`n" -Fore Cyan
- Write-Host "`t`t`t1. Refresh" -Fore Cyan
- Write-Host "`t`t`t2. Change Zip Code" -Fore Cyan
- Write-Host "`t`t`t3. Change Number of Days" -Fore Cyan
- Write-Host "`t`t`t4. Go to Main Menu`n" -Fore Cyan
- [int]$xMenuLevel2 = Read-Host "`t`tEnter Menu Option Number"
- }
- if( $xMenuLevel2 -lt 1 -or $xMenuLevel2 -gt 4 ){
- Write-Host "`tPlease select one of the options available.`n" -Fore Red; Start-Sleep -Seconds 1
- }
- Switch ($xMenuLevel2) {
- 1{ Get-Forecast $ZipCode $Days; pause }
- 2{ $ZipCode = Read-Host "`n`tEnter a zip code" }
- 3{ $Days = Read-Host "`n`tEnter the number of days to forecast" }
- default { break}
- }
- }
- default { exit }
- }
-
- LoadMenuSystem
-}
-
-LoadMenuSystem
\ No newline at end of file
diff --git a/Google/Get-GoogleHangoutsData.ps1 b/Google/Get-GoogleHangoutsData.ps1
new file mode 100644
index 0000000..40af555
--- /dev/null
+++ b/Google/Get-GoogleHangoutsData.ps1
@@ -0,0 +1,110 @@
+
+$HangoutsFilePath = '~\downloads\hangouts.json'
+$HangoutsRaw = Get-Content $HangoutsFilePath
+$HangoutsData = Get-Content $HangoutsFilePath | ConvertFrom-Json
+
+$Conversations = @()
+$AllParticipants = @()
+
+function Process-GoogleHangoutsData {
+ # First we want to get all participants, so we loop fully once
+ foreach ($Key in $HangoutsData.conversation_state) {
+ $Conversation = $HangoutsData.conversation_state.$Key.'conversation_state'.'conversation'
+
+ # Get all participants
+ foreach ($PersonData in $Conversation.'participant_data') {
+ $Person = $Conversation.'participant_data'.$PersonData
+ $Gaia_id = $Person.'id'.'gaia_id'
+ if (-not $person.'fallback_name' -or $Person.'fallback_name' -eq $null) { break }
+ if (-not $AllParticipants.$Gaia_id) {
+ $AllParticipants.$gaia_id = $Person.'fallback_name'
+ }
+ }
+ }
+ foreach ($Key in $HangoutsData.'conversation_state') {
+ $conversation_state = $HangoutsData.'conversation_state'.$key
+ $id = $conversation_state.'conversation_id'.'id'
+ $conversation = $conversation_state.'conversation_state'.'conversation'
+
+ # Find participants
+ $participants = @()
+ $participants_obj = @{}
+ foreach ($person_key in $conversation.'participant_data') {
+ $person = $conversation.'participant_data'.$person_key
+ $gaia_id = $person.'id'.'gaia_id'
+ $name = 'Unknown'
+ if ($person.'fallback_name') {
+ $name = $person.'fallback_name'
+ } else {
+ $name = $AllParticipants.$gaia_id
+ }
+ $participants.push($name)
+ $participants_obj.$gaia_id = $name
+ }
+ $participants_string = $participants.join(", ")
+ # Add to list
+ #$(".convo-list").append("" + participants_string + "")
+ # Parse events
+ $events = @()
+ for (event_key in conversation_state.'conversation_state'.'event'){
+ $convo_event = conversation_state.'conversation_state'.'event'.event_key
+ $timestamp = convo_event.'timestamp'
+ $msgtime = formatTimestamp(timestamp)
+ $sender = convo_event.'sender_id'.'gaia_id'
+ $message = ""
+ if(convo_event.'chat_message'){
+ # Get message
+ for(msg_key in convo_event.'chat_message'.'message_content'.'segment'){
+ $segment = convo_event.'chat_message'.'message_content'.'segment'.msg_key
+ if(segment.'type' == 'LINE_BREAK') $message += "\n"
+ if(!segment.'text') continue
+ message += twemoji.parse(segment.'text')
+ }
+ # Check for images on event
+ if(convo_event.'chat_message'.'message_content'.'attachment'){
+ foreach ($attach_key in convo_event.'chat_message'.'message_content'.'attachment'){
+ $attachment = convo_event.'chat_message'.'message_content'.'attachment'.attach_key
+ console.log(attachment)
+ if(attachment.'embed_item'.'type'.0 == "PLUS_PHOTO"){
+ message += "\n
"
+ }
+ }
+ }
+ events.push({msgtime: msgtime, sender: participants_obj.sender, message: message, timestamp: timestamp})
+ }
+ }
+ <# Sort events by timestamp
+ $events.sort(function($a, $b){
+ $keyA = $a.timestamp,
+ $keyB = $b.timestamp
+ if($keyA < $keyB) return -1
+ if($keyA > $keyB) return 1
+ return 0
+ })#>
+ # Add events
+ $Conversations.$id = $events
+ }
+}
+
+function switchConvo($id) {
+ $('.txt').text('')
+ foreach ($event_id in Conversations.id){
+ $convo_event = Conversations.id.event_id
+ $('.txt').append($convo_event.msgtime + ": " + $convo_event.sender + ": " + $convo_event.message + "\n")
+ }
+}
+
+function zeroPad($string) {
+ return ($string < 10) ? "0" + $string : $string
+}
+
+function formatTimestamp($timestamp) {
+ $d = new Date($timestamp/1000)
+ $formattedDate = $d.getFullYear() + "-" +
+ zeroPad($d.getMonth() + 1) + "-" +
+ zeroPad($d.getDate())
+ $hours = zeroPad($d.getHours())
+ $minutes = zeroPad($d.getMinutes())
+ $formattedTime = $hours + ":" + $minutes
+ return $formattedDate + " " + $formattedTime
+}
\ No newline at end of file
diff --git a/Install-Choco.ps1 b/Install-Choco.ps1
new file mode 100644
index 0000000..2a294ce
--- /dev/null
+++ b/Install-Choco.ps1
@@ -0,0 +1 @@
+iwr https://chocolatey.org/install.ps1 -UseBasicParsing | iex
\ No newline at end of file
diff --git a/Install-PoshSSH.ps1 b/Install-PoshSSH.ps1
new file mode 100644
index 0000000..7362d42
--- /dev/null
+++ b/Install-PoshSSH.ps1
@@ -0,0 +1,2 @@
+#iex (New-Object Net.WebClient).DownloadString("https://gist.github.com/darkoperator/6152630/raw/c67de4f7cd780ba367cccbc2593f38d18ce6df89/instposhsshdev")
+Install-Module -Name Posh-SSH
\ No newline at end of file
diff --git a/Install-PowerShellGet.ps1 b/Install-PowerShellGet.ps1
new file mode 100644
index 0000000..68eca4b
--- /dev/null
+++ b/Install-PowerShellGet.ps1
@@ -0,0 +1,3 @@
+# Install PowerShellGet from this link - https://www.microsoft.com/en-us/download/details.aspx?id=51451
+Install-PackageProvider NuGet -Force
+Import-PackageProvider NuGet -Force
\ No newline at end of file
diff --git a/MSO/Copy-SPOList.ps1 b/MSO/Copy-SPOList.ps1
new file mode 100644
index 0000000..8453e30
--- /dev/null
+++ b/MSO/Copy-SPOList.ps1
@@ -0,0 +1,66 @@
+function Copy-SPOList {
+ [cmdletbinding()]
+ param (
+ [Parameter(Mandatory = $true)]
+ [string]$SiteUrl,
+
+ [Parameter(Mandatory = $true)]
+ [string]$SourceListName,
+
+ [Parameter(Mandatory = $true)]
+ [string]$DestinationListName
+ )
+
+ Import-Module .\OneDrive.psm1
+ Import-SharePointClientComponents
+
+ $Context = New-Object Microsoft.SharePoint.Client.ClientContext($SiteUrl)
+ $Creds = Get-Credential
+ $SPOCreds = New-Object Microsoft.SharePoint.Client.SharePointOnlineCredentials($Creds.UserName, $Creds.Password)
+ $Context.Credentials = $SPOCreds
+
+ $SourceList = $Context.Web.Lists.GetByTitle($SourceListName)
+ $DestinationList = $Context.Web.Lists.GetByTitle($DestinationListName)
+ $ItemsToCopy = $SourceList.GetItems([Microsoft.SharePoint.Client.CamlQuery]::CreateAllItemsQuery())
+ $Fields = $SourceList.Fields
+
+ Write-Progress -Activity "Gathering all items from $SourceListName..."
+ $Context.Load($ItemsToCopy)
+ $Context.Load($SourceList)
+ $Context.Load($DestinationList)
+ $Context.Load($Fields)
+ $Context.ExecuteQuery()
+
+ $Count = $ItemsToCopy.Count
+ $Counter = 0
+
+ foreach ($Item in $ItemsToCopy) {
+ $Counter++
+
+ $SPOItem = New-Object -TypeName psobject
+ $SPOItem | Add-Member -Name ID -MemberType NoteProperty -Value $Item.ID
+
+ Write-Progress -Activity "Copying items from $SourceListName to $DestinationListName" `
+ -Status "Item $Counter of $Count - $($SPOItem.ID)" -PercentComplete (($Counter / $Count) * 100)
+
+ $ListItemInfo = New-Object Microsoft.SharePoint.Client.ListItemCreationInformation
+ $NewItem = $DestinationList.AddItem($ListItemInfo)
+
+ foreach ($Field in $Fields) {
+ if (
+ (-Not ($Field.ReadOnlyField)) -and
+ (-Not ($Field.Hidden)) -and
+ ($Field.InternalName -ne "Attachments") -and
+ ($Field.InternalName -ne "ContentType")
+ ) {
+ $SPOItem | Add-Member -Name $Field.InternalName -MemberType NoteProperty -Value $Item[$Field.InternalName]
+ $NewItem[$Field.InternalName] = $Item[$Field.InternalName]
+ $NewItem.update()
+ }
+ }
+
+ $SPOItem
+
+ $Context.ExecuteQuery()
+ }
+}
\ No newline at end of file
diff --git a/MSO/Get-DisabledLicensedUser.ps1 b/MSO/Get-DisabledLicensedUser.ps1
new file mode 100644
index 0000000..08f7f87
--- /dev/null
+++ b/MSO/Get-DisabledLicensedUser.ps1
@@ -0,0 +1,2 @@
+Connect-MsolService
+Get-MsolUser -All -EnabledFilter DisabledOnly | Where-Object { $_.IsLicensed }
\ No newline at end of file
diff --git a/MSO/Get-MFAEnabledUser.ps1 b/MSO/Get-MFAEnabledUser.ps1
new file mode 100644
index 0000000..216eef1
--- /dev/null
+++ b/MSO/Get-MFAEnabledUser.ps1
@@ -0,0 +1,18 @@
+Connect-MsolService
+
+Get-MsolUser -EnabledFilter EnabledOnly -All | ForEach-Object {
+ $AuthMethods = $_.StrongAuthenticationMethods
+ $DefaultMethod = ($AuthMethods | Where-Object -Property IsDefault -EQ $true).MethodType
+
+ New-Object -TypeName psobject -Property @{
+ UserPrincipalName = $_.UserPrincipalName
+ RelyingParty = $_.StrongAuthenticationRequirements.RelyingParty
+ RememberDevicesNotIssuedBefore = $_.StrongAuthenticationRequirements.RememberDevicesNotIssuedBefore
+ State = $_.StrongAuthenticationRequirements.State
+ DefaultMethod = $DefaultMethod
+ MethodType1 = $AuthMethods[0].MethodType
+ MethodType2 = $AuthMethods[1].MethodType
+ MethodType3 = $AuthMethods[2].MethodType
+ MethodType4 = $AuthMethods[3].MethodType
+ }
+}
\ No newline at end of file
diff --git a/MSO/Get-MSOLUsersWithAlias.ps1 b/MSO/Get-MSOLUsersWithAlias.ps1
new file mode 100644
index 0000000..451faf9
--- /dev/null
+++ b/MSO/Get-MSOLUsersWithAlias.ps1
@@ -0,0 +1,14 @@
+$Creds = Get-Credential
+$PSSession = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri https://ps.outlook.com/powershell/ `
+ -Credential $Creds -Authentication Basic –AllowRedirection
+
+Import-PSSession $PSSession
+
+$Results = Get-Mailbox | Select-Object DisplayName, @{
+ Name = 'EmailAddresses'
+ Expression = { ($_.EmailAddresses | Where-Object { $_ -LIKE "SMTP:*" }).TrimStart('SMTP:') }
+}
+
+$Results | Format-Table -AutoSize
+
+#$Results | Export-Csv c:\temp\UsersWithAlias.csv -NoTypeInformation
\ No newline at end of file
diff --git a/MSO/Remove-OneDriveIRM.ps1 b/MSO/Remove-OneDriveIRM.ps1
index d8d6ebf..c6711a7 100644
--- a/MSO/Remove-OneDriveIRM.ps1
+++ b/MSO/Remove-OneDriveIRM.ps1
@@ -1,7 +1,7 @@
# These 3 lines get the Documents site from SPO for the specified user
-$webUrl = "https://company-my.sharepoint.com/personal/company”
+$webUrl = "https://company-my.sharepoint.com/personal/company"
$clientContext = New-Object Microsoft.SharePoint.Client.ClientContext($webUrl)
-$list = $clientContext.Web.Lists.GetByTitle(“Documents")
+$list = $clientContext.Web.Lists.GetByTitle("Documents")
# reset the value to the default settings
$list.InformationRightsManagementSettings.Reset()
diff --git a/MSO/Set-License.ps1 b/MSO/Set-License.ps1
new file mode 100644
index 0000000..c2f0ee8
--- /dev/null
+++ b/MSO/Set-License.ps1
@@ -0,0 +1,95 @@
+<#
+.SYNOPSIS
+ Grant MSO license to users
+
+.DESCRIPTION
+
+.PARAMETER xx
+
+.EXAMPLE
+
+.NOTES
+ Created by Nick Rodriguez
+
+#>
+
+begin {
+ # https://support.microsoft.com/en-us/kb/3108269
+
+ # Connect to MS Online
+ Connect-MsolService
+
+ function Get-LicenseSkuId {
+ param ([string] $LicenseName)
+
+ # Get the license name
+ $LicenseObj = Get-MsolAccountSku | Where-Object {
+ $_.SkuPartNumber -eq $LicenseName -or
+ $_.AccountSkuId -eq $LicenseName
+ }
+ $LicenseObj.AccountSkuId
+ }
+
+ function Set-License {
+ [CmdletBinding(DefaultParameterSetName = 'SpecificUser')]
+ param (
+ [Parameter(Mandatory=$true)]
+ [ValidateSet('All','SpecificUser','Department')]
+ [string] $UserSet,
+
+ [Parameter(Mandatory=$true)]
+ [string] $LicenseSkuId,
+
+ [Parameter(Mandatory=$false, ParameterSetName = 'Group')]
+ [ValidateSet('All','EnabledOnly','DisabledOnly')]
+ [string] $FilterIsLicensed,
+
+ [Parameter(Mandatory=$false, ParameterSetName = 'SpecificUser')]
+ [string] $Email,
+
+ [Parameter(Mandatory=$false, ParameterSetName = 'Group')]
+ [string] $Department
+ )
+
+ switch ($UserSet) {
+ # Give license to all users
+ All { $Users = Get-MSOLUser -All -EnabledFilter $FilterIsLicensed }
+
+ # Get specific user based on email
+ SpecificUser { $Users = Get-MsolUser -UserPrincipalName $Email }
+
+ # Get specific user group based on department
+ Department { $Users = Get-MsolUser -Department $Department -EnabledFilter $FilterIsLicensed }
+ }
+
+ # Grant license to given users
+ $Users | Set-MsolUserLicense -AddLicenses $LicenseSkuId -Verbose
+
+ # Verify license was granted
+ $Users | Select UserPrincipalName, Licenses | ForEach-Object {
+ Write-Host "$($_.UserPrincipalName) - " -NoNewline
+
+ if ($_.Licenses.AccountSkuId -contains $LicenseSkuId) {
+ Write-Host $true -ForegroundColor Green
+ } else {
+ Write-Host $false -ForegroundColor Red
+ }
+ }
+ }
+}
+
+process {
+ $License = Get-LicenseSkuId -LicenseName 'enterprisewithscal'
+
+ # Add users from file
+ #Get-Content 'C:\users.txt' | ForEach-Object { Set-License -UserSet SpecificUser -LicenseSkuId $License -Email $_ }
+
+ # Add single user
+ #Set-License -UserSet SpecificUser -LicenseSkuId $License -Email 'me@company.com'
+
+ # Add department
+ #Set-License -UserSet Department -LicenseSkuId $License -Department 'IT' -FilterIsLicensed EnabledOnly
+
+ # All enabled users
+ Set-License -UserSet All -LicenseSkuId $License -FilterIsLicensed EnabledOnly
+}
\ No newline at end of file
diff --git a/MapDrivesUtility/Invoke-MapNetworkDrive.ps1 b/MapDrivesUtility/Invoke-MapNetworkDrive.ps1
new file mode 100644
index 0000000..2afd4e2
--- /dev/null
+++ b/MapDrivesUtility/Invoke-MapNetworkDrive.ps1
@@ -0,0 +1 @@
+This script has been moved to a new project repo - https://github.com/nickrod518/MapDrivesUtility
\ No newline at end of file
diff --git a/Munki/Create-MunkiRepo.ps1 b/Munki/Create-MunkiRepo.ps1
new file mode 100644
index 0000000..afe8191
--- /dev/null
+++ b/Munki/Create-MunkiRepo.ps1
@@ -0,0 +1,39 @@
+$Script = {
+ # Remove the port 80 binding on "Default Web Site" because we'll need that for our Munki repo
+ Try {
+ $Binding = Get-WebBinding -Port 80 -Name "Default Web Site"
+ Write-Verbose "Port 80 binding found on Default Web Site - removing..."
+ $Binding | Remove-WebBinding
+ } Catch {
+ Write-Verbose "Port 80 binding not found on Default Web Site - moving on..."
+ }
+
+ # Check if the Munki repo already exists
+ $MunkiExists = $false
+ Get-Website -Name "Munki" | ForEach-Object {
+ If ($_.Name -eq "Munki") { $MunkiExists = $true }
+ }
+
+ # Create the Munki repo if it doesn't exist
+ If ($MunkiExists) {
+ Write-Verbose "Munki repo already exists - moving on..."
+ } else {
+ Write-Verbose "Munki repo doesn't exist - creating..."
+
+ # Create site directory
+ New-Item -ItemType Directory "R:\repo" -Force
+
+ # Create the new Munki website
+ New-Website -Name "Munki" -PhysicalPath "R:\"
+
+ # Set MIME types
+ Add-WebConfigurationProperty -PSPath IIS:\Sites\Munki -Filter system.webServer/staticContent -Name '.' -Value `
+ @{ fileExtension = '.'; mimeType = 'application/octet-stream' }, # Accept all extensions
+ @{ fileExtension = '*'; mimeType = 'application/octet-stream' } # Accept all files
+
+ # Allow Directory Browsing
+ Set-WebConfigurationProperty -PSPath IIS:\sites\Munki -Filter system.webServer/directoryBrowse -Name enabled -Value 'true'
+ }
+}
+
+Invoke-Command -ComputerName server01 -ScriptBlock $Script
\ No newline at end of file
diff --git a/Munki/Get-MunkiRepoIP.ps1 b/Munki/Get-MunkiRepoIP.ps1
new file mode 100644
index 0000000..57afcb2
--- /dev/null
+++ b/Munki/Get-MunkiRepoIP.ps1
@@ -0,0 +1,22 @@
+# List of servers with Munki repos setup
+$Servers = (Get-ADGroupMember -Identity MunkiRepos).Name
+
+$Creds = Get-Credential
+
+foreach ($Computer in $Servers) {
+ if(Test-Connection -ComputerName $Computer -Count 1 -ea 0) {
+ try {
+ $Networks = Get-WmiObject Win32_NetworkAdapterConfiguration -ComputerName $Computer -EA Stop -Credential $Creds | ? {$_.IPEnabled}
+ } catch {
+ Write-Warning "Error occurred while querying $Computer."
+ Continue
+ }
+ foreach ($Network in $Networks) {
+ $IPAddress = $Network.IpAddress[0]
+ $OutputObj = New-Object -Type PSObject
+ $OutputObj | Add-Member -MemberType NoteProperty -Name Repo -Value $Computer.ToUpper()
+ $OutputObj | Add-Member -MemberType NoteProperty -Name IPAddress -Value $IPAddress
+ $OutputObj
+ }
+ }
+ }
\ No newline at end of file
diff --git a/Munki/Set-Repo b/Munki/Set-Repo
new file mode 100644
index 0000000..92b807e
--- /dev/null
+++ b/Munki/Set-Repo
@@ -0,0 +1,45 @@
+#!/bin/bash
+
+# Get our log file, or create one
+Log="/Users/Shared/Munki/MunkiRepo.log"
+if [ ! -f $Log ]; then
+ mkdir -p /Users/Shared/Munki/
+ touch $Log
+fi
+
+# Get the active network interface
+ActiveIFace=`route get google.com | awk '/interface/ {print $2}'`
+echo "`date`: The active interface is $ActiveIFace" >> $Log
+
+# Get the current IP address
+CurrentIP=`ifconfig $ActiveIFace | awk '/inet / {print $2}'`
+echo "`date`: Your IP address is $CurrentIP" >> $Log
+
+# Office IP addresses associative array
+Locations=(
+ '10.20.0::munkirepo01' # Region1
+ '10.40.0::munkirepo02' # Region2
+ '192.168.1::munkirepo03' # VPN
+)
+
+# Set fallback repo if no matches are found
+Repo='munkirepo01'
+
+# Go through each IP in our locations map to check for matches
+for Location in "${Locations[@]}" ; do
+ # First 3 octets of repo's IP
+ IP=${Location%%::*}
+
+ # Check if current IP contains match
+ if [[ "$CurrentIP" == "$IP"* ]]; then
+ Repo=${Location##*::}
+ break
+ fi
+done
+
+# Update the Munki preferences to match the new repo
+{
+ defaults write /Library/Preferences/ManagedInstalls.plist SoftwareRepoURL "http://$Repo.company.local/repo" && echo "`date`: Repo set to $Repo" >> $Log
+} || {
+ echo "`date`: Error setting repo to $Repo" >> $Log
+}
diff --git a/Munki/Sync-MunkiRepos.ps1 b/Munki/Sync-MunkiRepos.ps1
new file mode 100644
index 0000000..6259e64
--- /dev/null
+++ b/Munki/Sync-MunkiRepos.ps1
@@ -0,0 +1,86 @@
+function Mail-Results {
+ [CmdletBinding()]
+ param (
+ [Parameter(Mandatory=$true)]
+ [string]
+ $MessageBody,
+
+ [Parameter(Mandatory=$true)]
+ [string[]]
+ $Attachments
+ )
+
+ $SMTPServer = 'smtp1.corp.local'
+ $SMTP = New-Object Net.Mail.SmtpClient($SMTPServer)
+
+ $Message = New-Object Net.Mail.MailMessage
+ $Message.From = 'MunkiAlerts@company.com'
+ $Message.To.Add('me@company.com')
+ $Message.Subject = 'Munki Sync Results'
+ $Message.Body = $MessageBody
+ foreach ($Report in $Attachments) {
+ $Message.Attachments.Add($Report)
+ }
+ $SMTP.Send($Message)
+}
+
+function Convert-RobocopyExitCode ($ExitCode) {
+ switch ($ExitCode) {
+ 16 {'***FATAL ERROR***'}
+ 15 {'OKCOPY + FAIL + MISMATCHES + XTRA'}
+ 14 {'FAIL + MISMATCHES + XTRA'}
+ 13 {'OKCOPY + FAIL + MISMATCHES'}
+ 12 {'FAIL + MISMATCHES'}
+ 11 {'OKCOPY + FAIL + XTRA'}
+ 10 {'FAIL + XTRA'}
+ 9 {'OKCOPY + FAIL'}
+ 8 {'FAIL'}
+ 7 {'OKCOPY + MISMATCHES + XTRA'}
+ 6 {'MISMATCHES + XTRA'}
+ 5 {'OKCOPY + MISMATCHES'}
+ 4 {'MISMATCHES'}
+ 3 {'OKCOPY + XTRA'}
+ 2 {'XTRA'}
+ 1 {'OKCOPY'}
+ 0 {'No Change'}
+ default {'Unknown'}
+ }
+}
+
+# Create our log directory
+$LogDir = New-Item -ItemType Directory "$PSScriptRoot\Logs" -Force
+
+# Repo servers that are currently standing
+$Servers = @(
+ 'munkirepo01',
+ 'munkirepo02',
+ 'munkirepo03'
+)
+
+$MessageBody = ''
+$Logs = @()
+$SourceRepo = '\\macserver.company.local\repo'
+
+# Distribute content from the central repo to each node repo
+foreach ($Repo in $Servers) {
+ $Log = (New-Item -ItemType File "$LogDir\MunkiSync-$Repo-$( (Get-Date).ToString('yyyyMMdd-HHmm') ).log").FullName
+
+ # Update repo
+ ROBOCOPY $SourceRepo "\\$Repo\r$\repo" /DCOPY:DA /MIR /FFT /Z /XA:SH /R:10 /LOG:$Log /XJD
+ $ExitCode = $LASTEXITCODE
+
+ # Get volume info
+ $Volume = Get-WmiObject Win32_Volume -ComputerName $Repo | Where-Object { $_.Name -eq 'R:\' } | Select-Object Name, Capacity, FreeSpace
+
+ $Results += @([pscustomobject]@{
+ Repo = $Repo
+ 'RoboCopy Results' = "$ExitCode`: $(Convert-RobocopyExitCode $ExitCode)"
+ 'Free (MB)' = [math]::truncate($Volume.FreeSpace / 1MB)
+ 'Capacity (MB)' = [math]::truncate($Volume.Capacity / 1MB)
+ 'Used (MB)' = [math]::truncate(($Volume.Capacity - $Volume.FreeSpace) / 1MB)
+ 'Completion Time' = Get-Date
+ })
+ $Logs += $Log
+}
+
+Mail-Results -MessageBody ($Results | Format-Table -AutoSize | Out-String) -Attachments $Logs
\ No newline at end of file
diff --git a/Munki/installmunki b/Munki/installmunki
new file mode 100644
index 0000000..ef4fc05
--- /dev/null
+++ b/Munki/installmunki
@@ -0,0 +1,55 @@
+#!/bin/bash
+
+##
+# Download and install the latest Munki2 tools from munkibuilds.org
+##
+
+cat < $log" >> $crontab
+ fi
+else
+ echo "SHELL=/bin/sh" > $crontab
+ echo "PATH=/bin:/sbin:/usr/bin:/usr/sbin" >> $crontab
+ echo "30 * * * * root /usr/sbin/softwareupdate -l &> $log" >> $crontab
+fi
+
+/usr/bin/tail -1 $log
diff --git a/Munki/updatemunkirepo b/Munki/updatemunkirepo
new file mode 100644
index 0000000..76e8e9c
--- /dev/null
+++ b/Munki/updatemunkirepo
@@ -0,0 +1,28 @@
+#!/bin/bash
+
+cat <
+
+Pause
\ No newline at end of file
diff --git a/Office/Enable-OutlookQuarantineFolder.ps1 b/Office/Enable-OutlookQuarantineFolder.ps1
new file mode 100644
index 0000000..ec6d164
--- /dev/null
+++ b/Office/Enable-OutlookQuarantineFolder.ps1
@@ -0,0 +1,50 @@
+begin { Start-Transcript $env:TEMP\olQuarantineEnable.log }
+
+process {
+ # Get Outlook object
+ Add-Type -AssemblyName Microsoft.Office.Interop.Outlook
+ $Outlook = New-Object -ComObject Outlook.Application
+
+ # Get Inbox
+ $Namespace = $Outlook.GetNamespace('MAPI')
+ $Inbox = $Namespace.GetDefaultFolder([Microsoft.Office.Interop.Outlook.OlDefaultFolders]::olFolderInbox)
+
+ try {
+ # Create new Quarantine folder
+ $Quarantine = $Inbox.Folders.Add('Quarantine')
+ } catch {
+ # Get existing Quarantine folder
+ $Quarantine = $Inbox.Folders | Where-Object -Property Name -eq 'Quarantine'
+ }
+
+ # Set the web Url to the Quarantine admin site and set web view as default
+ $Quarantine.WebViewURL = 'https://admin.protection.outlook.com/quarantine'
+ $Quarantine.WebViewOn = $true
+
+ # Add Quarantine folder to favorites
+ $OutlookModule = $Outlook.ActiveExplorer().NavigationPane.Modules.Item('Mail')
+ $Favorites = $OutlookModule.NavigationGroups.Item('Favorites')
+ $Favorites.NavigationFolders.Add($Quarantine)
+
+ # Validate settings
+ $Quarantine = $Inbox.Folders | Where-Object -Property Name -eq 'Quarantine'
+
+ if ($Quarantine) {
+ if ($Quarantine.WebViewURL -ne 'https://admin.protection.outlook.com/quarantine') { exit 996 }
+
+ if (-not $Quarantine.WebViewOn) { exit 997 }
+
+ if (-not $Favorites.NavigationFolders.Item($Quarantine.Name)) { exit 998 }
+
+ exit 0
+ } else {
+ exit 995
+ }
+}
+
+end {
+ # Used as detection method in SCCM
+ New-Item -Path 'HKLM:\Software\SMS' -ItemType Key -Force |
+ New-ItemProperty -Name olQuarantineEnabled -Value $LASTEXITCODE -PropertyType String -Force
+ Stop-Transcript
+}
\ No newline at end of file
diff --git a/README.md b/README.md
index ed8b329..203aa3f 100644
--- a/README.md
+++ b/README.md
@@ -1,3 +1,3 @@
# PowerShell-Scripts
-Just a bunch of PowerShell scripts that help me get through the day.
+Just a bunch of PowerShell scripts for sysadmins.
diff --git a/Registry/Check-DotNetVersion.ps1 b/Registry/Check-DotNetVersion.ps1
new file mode 100644
index 0000000..1d2af48
--- /dev/null
+++ b/Registry/Check-DotNetVersion.ps1
@@ -0,0 +1,6 @@
+$version = (Get-ItemProperty 'HKLM:\SOFTWARE\Microsoft\NET Framework Setup\NDP\v4\Full' -Name Version -ErrorAction SilentlyContinue).Version
+if ($version -like "4.5*") {
+ return $true
+} else {
+ return $false
+}
\ No newline at end of file
diff --git a/Registry/Disable-UAC.ps1 b/Registry/Disable-UAC.ps1
new file mode 100644
index 0000000..c40aec7
--- /dev/null
+++ b/Registry/Disable-UAC.ps1
@@ -0,0 +1,3 @@
+New-ItemProperty -Path HKLM:Software\Microsoft\Windows\CurrentVersion\policies\system -Name EnableLUA -PropertyType DWord -Value 0 -Force
+Write-Host "You must restart before changes will be in effect."
+pause
diff --git a/Registry/Suspend-SS.ps1 b/Registry/Suspend-SS.ps1
new file mode 100644
index 0000000..f83a552
--- /dev/null
+++ b/Registry/Suspend-SS.ps1
@@ -0,0 +1,54 @@
+[System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms") | Out-Null
+[string]$regPath = 'HKCU:\Software\Policies\Microsoft\Windows\Control Panel\Desktop\'
+[string]$Status = "Time before screen saver default settings are restored:"
+
+# Validate input; must be between 15-240
+do { [int]$Minutes = Read-Host "Minutes to suspend screen saver (15-240)" }
+while ((15..240) -notcontains $Minutes)
+
+[Int32]$Seconds = $Minutes * 60
+[string]$Message = "Screen saver suspended for $Minutes minutes..."
+
+function Set-SS {
+ Param (
+ [Parameter(Mandatory=$true)]
+ [int]
+ $TimeOut,
+
+ [Parameter(Mandatory=$true)]
+ [int]
+ $Active,
+
+ [Parameter(Mandatory=$true)]
+ [int]
+ $IsSecure
+ )
+
+ try {
+ # Set screen saver registry properties
+ Set-ItemProperty -Path $regPath -Name ScreenSaveTimeOut -Value $TimeOut -ErrorAction Stop
+ Set-ItemProperty -Path $regPath -Name ScreenSaveActive -Value $Active -ErrorAction Stop
+ Set-ItemProperty -Path $regPath -Name ScreenSaverIsSecure -Value $IsSecure -ErrorAction Stop
+ } catch {
+ Write-Host 'There was an issue disabling the screen saver:'
+ Write-Host $_.Exception.Message -ForegroundColor Red
+ Read-Host 'Press [Enter] to exit'
+ exit
+ }
+}
+
+# Disable screen saver
+Set-SS -TimeOut 0 -Active 0 -IsSecure 0
+
+foreach ($Sec in (1..$Seconds)) {
+ # Update progress bar every second
+ Write-Progress -Activity $Message -Status $Status -SecondsRemaining ($Seconds - $Sec)
+
+ # Disable screen saver every 5 minutes
+ if ( !($Sec % 300) ) { Set-SS -TimeOut 0 -Active 0 -IsSecure 0 }
+
+ Start-Sleep -Seconds 1
+
+ # Restore screen saver during last second... literally
+ if ($Sec -eq $Seconds) { Set-SS -TimeOut 900 -Active 1 -IsSecure 1 }
+}
\ No newline at end of file
diff --git a/Registry/Suspend-SSWithAuth.ps1 b/Registry/Suspend-SSWithAuth.ps1
new file mode 100644
index 0000000..ad9c5c0
--- /dev/null
+++ b/Registry/Suspend-SSWithAuth.ps1
@@ -0,0 +1,53 @@
+[System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms") | Out-Null
+
+#Get User's DN
+$objSearcher = New-Object System.DirectoryServices.DirectorySearcher
+$objSearcher.SearchRoot = New-Object System.DirectoryServices.DirectoryEntry
+$objSearcher.Filter = "(&(objectCategory=user)(SamAccountname=$($env:USERNAME)))"
+$objSearcher.SearchScope = "Subtree"
+$obj = $objSearcher.FindOne()
+$User = $obj.Properties["distinguishedname"]
+
+#Now get the members of the group
+$Group = "Workstation Admins"
+$objSearcher.Filter = "(&(objectCategory=group)(SamAccountname=$Group))"
+$objSearcher.SearchScope = "Subtree"
+$obj = $objSearcher.FindOne()
+[String[]]$Members = $obj.Properties["member"]
+
+If ($Members -notcontains $User) {
+ [System.Windows.Forms.MessageBox]::Show("You are not authorized to run this.", "Suspend Screensaver", 0)
+} Else {
+ [string]$regPath = 'HKCU:\Software\Policies\Microsoft\Windows\Control Panel\Desktop\'
+ [string]$status = "Time before screen saver default settings are restored:"
+
+ # validate input; must be between 15-240
+ do { [int]$minutes = Read-Host "Minutes to suspend screen saver (15-240)" }
+ while ((15..240) -notcontains $minutes)
+
+ [Int32]$seconds = $minutes * 60
+ [string]$message = "Screen saver suspended for $minutes minutes..."
+
+ # function for setting screen saver properties... saves lines later
+ function Set-SS {
+ Set-ItemProperty -Path $regPath -Name ScreenSaveTimeOut -Value $args[0]
+ Set-ItemProperty -Path $regPath -Name ScreenSaveActive -Value $args[1]
+ Set-ItemProperty -Path $regPath -Name ScreenSaverIsSecure -Value $args[2]
+ }
+
+ # disable screen saver
+ Set-SS 0 0 0
+
+ ForEach ($sec in (1..$seconds)) {
+ # update progress bar every second
+ Write-Progress -Activity $message -Status $status -SecondsRemaining ($seconds - $sec)
+
+ # disable screen saver every 5 minutes
+ If ( !($sec % 300) ) { Set-SS 0 0 0 }
+
+ Start-Sleep -Seconds 1
+
+ # restore screen saver during last second... literally
+ If ($sec -eq $seconds) { Set-SS 900 1 1 }
+ }
+}
diff --git a/RemoteApp/Install-Apps.ps1 b/RemoteApp/Install-Apps.ps1
new file mode 100644
index 0000000..52e6fad
--- /dev/null
+++ b/RemoteApp/Install-Apps.ps1
@@ -0,0 +1,132 @@
+$url = 'RDSBROKER.COMPANY.COM'
+$domain = 'DOMAIN'
+
+Write-Host "Cleaning up previous RemoteApp install..."
+
+try {
+ $feeds = Get-ChildItem 'HKCU:\Software\Microsoft\Workspaces\Feeds' -ErrorAction Stop
+}
+catch {
+ Write-Host "No feeds found"
+}
+
+if ($feeds) {
+ foreach ($feed in $feeds) {
+ $id = (Get-ItemProperty $feed.PSPath -Name WorkspaceId).WorkspaceId
+
+ if ($id -eq $url) {
+ Write-Host "Previous install found"
+
+ Write-Host "Removing Workspace folder..."
+ $workspaceFolder = (Get-ItemProperty $feed.PSPath -Name WorkspaceFolder).WorkspaceFolder
+ Remove-Item $workspaceFolder -Recurse -ErrorAction SilentlyContinue
+
+ Write-Host "Removing Desktop icons..."
+ $startFolder = (Get-ItemProperty $feed.PSPath -Name StartMenuRoot).StartMenuRoot
+ $apps = Get-ChildItem $startFolder
+ $desktopIcons = Get-ChildItem "$env:USERPROFILE\Desktop"
+ foreach ($icon in $desktopIcons) {
+ if ($apps.Name -contains $icon.Name) {
+ Remove-Item $icon.FullName
+ }
+ }
+
+ Write-Host "Removing Start Menu items..."
+ Remove-Item $startFolder -Recurse
+
+ Write-Host "Removing registry items..."
+ Remove-Item $feed.PSPath -Recurse
+ }
+
+ Write-Host "Cleanup complete"
+
+ break
+ }
+}
+
+Write-Host "`n`nEnter your credentials..."
+
+try {
+ $creds = Get-Credential -Credential $null
+}
+catch {
+ Write-Warning "You must enter credentials to complete the setup"
+ Read-Host "`n`nPress [Enter] to exit"
+ exit
+}
+
+$username = $creds.UserName
+$password = $creds.GetNetworkCredential().Password
+
+Write-Host "`n`nAdding credentials to Credential Manager..."
+cmdkey /add:$url /user:$domain$username /password:$password
+cmdkey /add:*.company.com /user:$domain$username /password:$password
+cmdkey /add:TERMSRV/$url /user:$domain$username /password:$password
+
+Write-Host "`n`nSetting up Workspace..."
+
+$winVer = [System.Environment]::OSVersion.Version.Major
+if ($winVer -ne "10" ) {
+ Write-Host "Windows 10 not detected"
+ $userProf = "$env:APPDATA\Microsoft\Windows\Start Menu\Programs\RemoteApp and Desktop Connections\Work Resources\"
+}
+else {
+ Write-Host "Windows 10 detected"
+ $userProf = "$env:APPDATA\Microsoft\Windows\Start Menu\Programs\Work Resources (RADC)"
+}
+
+# Create the wcx file
+Write-Host "Creating setup wcx file..."
+$wcxPath = "$env:TEMP\RDSWebSetup.wcx"
+$config = @"
+
+
+
+
+"@
+New-Item -Path $wcxPath -ItemType "File" -Value $config -Force | Out-Null
+
+# Silently run the RemoteApp config
+Write-Host "Running wcx setup..."
+rundll32.exe tsworkspace, WorkspaceSilentSetup $wcxPath
+
+
+
+# Wait until the icons appear in the user profile and then copy them to the desktop
+Write-Host "Waiting for Workspace icons to be created..."
+$counter = 0
+$timeout = $false
+
+while (-not (Test-Path $userProf) -and $counter -lt 15) {
+ Start-Sleep -Seconds 2
+ $counter++
+}
+
+if ($counter -eq 15) {
+ $timeout = $true
+}
+
+$found = $false
+
+$feeds = Get-ChildItem 'HKCU:\Software\Microsoft\Workspaces\Feeds'
+foreach ($feed in $feeds) {
+ $id = (Get-ItemProperty $feed.PSPath -Name WorkspaceId).WorkspaceId
+
+ if ($id -eq $url) {
+ $found = $true
+ }
+}
+
+if (-not $found -or $timeout) {
+ Write-Host "`n`nCredentials invalid or timeout reached. Please follow the instructions in the new window..." -ForegroundColor Red
+
+ Start-Process $wcxPath -Wait
+}
+
+Remove-Item $wcxPath
+
+Write-Host "`n`nCopying icons to desktop..."
+Copy-Item "$userProf\*" "$env:USERPROFILE\Desktop\" -Recurse -Force
+
+Read-Host "`n`nPress [Enter] to exit"
+exit
\ No newline at end of file
diff --git a/RemoteApp/Update-Apps.ps1 b/RemoteApp/Update-Apps.ps1
new file mode 100644
index 0000000..c709592
--- /dev/null
+++ b/RemoteApp/Update-Apps.ps1
@@ -0,0 +1,24 @@
+$url = 'RDSBROKER.COMPANY.COM'
+
+$feeds = Get-ChildItem 'HKCU:\Software\Microsoft\Workspaces\Feeds'
+foreach ($feed in $feeds) {
+ $id = (Get-ItemProperty $feed.PSPath -Name WorkspaceId).WorkspaceId
+
+ if ($id -eq $url) {
+ # Remove Start folder and desktop icons
+ $startFolder = (Get-ItemProperty $feed.PSPath -Name StartMenuRoot).StartMenuRoot
+ $apps = Get-ChildItem $startFolder
+ $desktopIcons = Get-ChildItem "$env:USERPROFILE\Desktop"
+ foreach ($icon in $desktopIcons) {
+ if ($apps.Name -contains $icon.Name) {
+ Remove-Item $icon.FullName
+ }
+ }
+ }
+}
+
+rundll32 tsworkspace,TaskUpdateWorkspaces2
+
+Start-Sleep -Seconds 2
+
+Copy-Item "$startFolder\*" "$env:USERPROFILE\Desktop\" -Recurse -Force
\ No newline at end of file
diff --git a/Retire-CMApplication.ps1 b/Retire-CMApplication.ps1
index a299076..ddfe82d 100644
--- a/Retire-CMApplication.ps1
+++ b/Retire-CMApplication.ps1
@@ -6,7 +6,7 @@ function Retire-CMApplication {
)
# import cm module
- Import-Module 'C:\Program Files (x86)\Microsoft Configuration Manager\AdminConsole\bin\ConfigurationManager.psd1'
+ Import-Module ($Env:SMS_ADMIN_UI_PATH.Substring(0,$Env:SMS_ADMIN_UI_PATH.Length-5) + '\ConfigurationManager.psd1')
# change to the cm site drive
$PSD = Get-PSDrive -PSProvider CMSite
diff --git a/SCCM/Invoke-CMDeploymentSummary.ps1 b/SCCM/Invoke-CMDeploymentSummary.ps1
new file mode 100644
index 0000000..19189c8
--- /dev/null
+++ b/SCCM/Invoke-CMDeploymentSummary.ps1
@@ -0,0 +1,35 @@
+[CmdletBinding()]
+Param(
+ [Parameter(Mandatory = $true)]
+ [string]$CollectionName,
+
+ [Parameter(Mandatory = $true)]
+ [pscredential]$Credential
+)
+
+Import-Module "C:\powershell-scripts\dependencies\ConfigurationManager.psd1"
+$StartingLocation = Get-Location
+Set-Location -Path "$(Get-PSDrive -PSProvider CMSite):\" -ErrorAction Stop
+
+Invoke-Command -Credential $Credential -ComputerName dcsccm03 -ScriptBlock {
+ Param($CollectionName)
+
+ Import-Module "C:\powershell-scripts\dependencies\ConfigurationManager.psd1"
+ $StartingLocation = Get-Location
+ Set-Location -Path "$(Get-PSDrive -PSProvider CMSite):\" -ErrorAction Stop
+
+ Invoke-CMDeploymentSummarization -CollectionName $CollectionName -Verbose
+
+ Set-Location $StartingLocation
+
+ Start-Sleep -Seconds 10
+} -ArgumentList $CollectionName
+
+Get-CMDeployment -CollectionName $CollectionName |
+ Select-Object -Property ApplicationName, NumberSuccess, NumberTargeted, SummarizationTime,
+ @{
+ Name = 'DeploymentSummary'
+ Expression = { "{0:P0}" -f ($_.NumberSuccess / $_.NumberTargeted) }
+ }
+
+Set-Location $StartingLocation
\ No newline at end of file
diff --git a/SCCM/Manage-CMMobileDevice.ps1 b/SCCM/Manage-CMMobileDevice.ps1
new file mode 100644
index 0000000..9596708
--- /dev/null
+++ b/SCCM/Manage-CMMobileDevice.ps1
@@ -0,0 +1,361 @@
+# Search and replace the following strings with whatever's appropriate for your environment
+# CMSERVER, DOMAINNAME, MOBILEDEVICECOLLECTIONID
+
+# Update the output log that the user sees, and scroll to keep up
+function Update-Log {
+ param(
+ [string]$Text,
+
+ [Parameter(Mandatory = $false)]
+ #[ValidateSet('White', 'Yellow', 'Red', 'Green')]
+ [string]$Color = 'White',
+
+ [switch]$NoNewLine
+ )
+
+ $LogTextBox.SelectionColor = $Color
+ $LogTextBox.AppendText("$Text")
+ if (-not $NoNewLine) { $LogTextBox.AppendText("`n") }
+ $LogTextBox.Update()
+ $LogTextBox.ScrollToCaret()
+}
+
+# Verify that the username exists
+function Test-User {
+ param([string]$UserID)
+
+ $UserObject = Get-CMUser -Name "DOMAINNAME\$UserID"
+
+ if ($UserObject -ne $null) {
+ Update-Log "$UserID is a valid user."
+
+ return $UserObject
+ } else {
+ if ($UserID -eq '') {
+ Update-Log "User ID field is blank." -Color Red -NoNewLine
+ } else {
+ Update-Log "$UserID was not found." -Color Red -NoNewLine
+ }
+
+ Update-Log " Aborted - no actions performed.`n" -Color Red
+
+ return
+ }
+}
+
+# verify that the device exists
+function Test-Device {
+ param([string]$DeviceID)
+
+ $DeviceObject = Get-CMDevice -ResourceId $DeviceID
+
+ if ($DeviceObject -ne $null) {
+ Update-Log "$DeviceID is a valid device ID."
+ return $DeviceObject
+ } else {
+ if ($DeviceID -eq '') {
+ Update-Log "device ID field is blank." -Color Red -NoNewLine
+ } else {
+ Update-Log "$DeviceID was not found." -Color Red -NoNewLine
+ }
+
+ Update-Log " Aborted - no actions performed." -Color Red
+
+ return
+ }
+}
+
+# allow the user to use "*" to search for usernames and select the one they want
+function Get-User {
+ param([string]$UserID)
+
+ $Users = Get-CMUser -Name "DOMAINNAME\$UserID"
+
+ if ($Users -ne $null) {
+ try {
+ $Selected = if ($Users.Count -gt 1) {
+ $Users.Name | Out-GridView -Title 'Select a user' -OutputMode Single
+ } else {
+ $Users.Name
+ }
+
+ $Selected = $Selected.Replace("DOMAINNAME\","")
+ $Selected = $Selected.Split(" ")[0]
+ $UserTextBox.Text = $Selected
+
+ Update-Log "Updated user ID field to $Selected."
+ } catch { }
+ } else {
+ if ($UserID -eq '') {
+ Update-Log "User ID field is blank." -Color Yellow
+ } else {
+ Update-Log "No users were found with ID $UserID." -Color Yellow
+ }
+ }
+}
+
+# get the mobile devices assigned to the user
+function Get-UserDevices {
+ param([string]$UserID)
+
+ $UserObject = Test-User $UserID
+
+ Try {
+ # Get all the primary devices for the user
+ $PrimaryDevices = Get-CMUserDeviceAffinity -UserId $UserObject.ResourceID
+
+ # With those id's, create a list of device objects
+ Update-Log 'Searching for devices' -NoNewLine
+
+ $Devices = foreach ($Device in $PrimaryDevices) {
+ Get-CMDevice -ResourceID $Device.ResourceID
+ Update-Log '.' -NoNewLine
+ }
+
+ Update-Log ''
+
+ # Filter the objects list so we're only looking at mobile devices not equal to OS X
+ $MobileDevices = $Devices | Where-Object { ($_.ClientType -eq 3) -and ($_.DeviceOS -notlike "OS X*") }
+
+ # Output what we have and let
+ $Selected = if ($MobileDevices.Count -gt 1) {
+ $MobileDevices | Select-Object -Property ResourceID, Name, DeviceOS, LastActiveTime, WipeStatus | Out-GridView -Title 'Select a device' -OutputMode Single
+ } else {
+ $MobileDevices
+ }
+
+ if ($Selected) {
+ $DeviceIDTextBox.Text = $Selected.ResourceID
+ $DeviceNameTextBox.Text = $Selected.Name
+ Update-Log "Set device field to resource ID $($Selected.ResourceID) and name field to $($Selected.Name)."
+ } else {
+ Update-Log "No mobile devices found for $UserID."
+ }
+ } Catch {
+ Update-Log "There was a problem trying to update the device ID and name fields." -Color Yellow
+ }
+}
+
+# generate a list of all mobile devices inactive for 30+ days
+function Get-InactiveDevices {
+ Update-Log 'Searching for devices' -NoNewLine
+ Get-CMDevice -CollectionId 'MOBILEDEVICECOLLECTIONID' | Where-Object { $_.LastActiveTime -lt (Get-Date).AddDays(-30) } | ForEach-Object {
+ New-Object PSObject -Property @{
+ UserName = (Get-CMUserDeviceAffinity -DeviceId $_.ResourceID).UniqueUserName.Replace("DOMAINNAME\","")
+ ResourceID = [string]$_.ResourceID
+ Name = $_.Name
+ DeviceOS = $_.DeviceOS
+ LastActiveTime = $_.LastActiveTime
+ WipeStatus = $_.WipeStatus
+ }
+
+ Update-Log '.' -NoNewLine
+ } | Select-Object -Property UserName, ResourceID, Name, DeviceOS, LastActiveTime, WipeStatus | Out-GridView -Title 'Inactive devices'
+
+ Update-Log ''
+}
+
+function Write-Title {
+ Update-Log " __ ___ __ _ __ ___ _ " -Color Orange
+ Update-Log " / |/ /__ / / (_) /__ / _ \___ _ __(_)______ " -Color Orange
+ Update-Log " / /|_/ / _ \/ _ \/ / / -_) / // / -_) |/ / / __/ -_)" -Color Orange
+ Update-Log " /_/__/_/\___/_.__/_/_/\__/ /____/\__/|___/_/\__/\__/ " -Color Orange
+ Update-Log " / |/ /__ ____ ___ ____ ____ __ _ ___ ___ / /_ " -Color Orange
+ Update-Log " / /|_/ / _ ``/ _ \/ _ ``/ _ ``/ -_) ' \/ -_) _ \/ __/ " -Color Orange
+ Update-Log " /_/ /_/\_,_/_//_/\_,_/\_, /\__/_/_/_/\__/_//_/\__/ v1.2" -Color Orange
+ Update-Log " /___/ " -Color Orange -NoNewLine
+ Update-Log " by Nick Rodriguez " -Color Gold
+ Update-Log ''
+}
+
+function New-UtilityForm {
+ # References for building forms
+ [void] [System.Reflection.Assembly]::LoadWithPartialName("System.Drawing")
+ [void] [System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")
+
+ $Form = New-Object System.Windows.Forms.Form
+ $Form.FormBorderStyle = 'FixedDialog'
+ $Form.Text = 'Config Manager Mobile Device Management'
+ $Form.Size = New-Object System.Drawing.Size(490, 590)
+ $Form.StartPosition = "CenterScreen"
+
+ # Creates output textbox
+ $LogTextBox = New-Object System.Windows.Forms.RichTextBox
+ $LogTextBox.Location = New-Object System.Drawing.Size(12, 120)
+ $LogTextBox.Size = New-Object System.Drawing.Size(460, 430)
+ $LogTextBox.ReadOnly = 'True'
+ $LogTextBox.BackColor = 'Black'
+ $LogTextBox.ForeColor = 'White'
+ $LogTextBox.Font = 'Consolas'
+ $Form.Controls.Add($LogTextBox)
+
+ # User id input
+ $UserSearchButton = New-Object System.Windows.Forms.Button
+ $UserSearchButton.Location = New-Object System.Drawing.Size(12, 14)
+ $UserSearchButton.Size = New-Object System.Drawing.Size(75, 22)
+ $UserSearchButton.Text = "User ID"
+ $UserSearchButton.Add_Click(
+ { Script:Get-User $UserTextBox.Text })
+ $Form.Controls.Add($UserSearchButton)
+
+ $UserTextBox = New-Object System.Windows.Forms.TextBox
+ $UserTextBox.Location = New-Object System.Drawing.Size(110, 15)
+ $UserTextBox.Size = New-Object System.Drawing.Size(60, 20)
+ $Form.Controls.Add($UserTextBox)
+
+ # Button to search for devices assigned to user
+ $DeviceSearchButton = New-Object System.Windows.Forms.Button
+ $DeviceSearchButton.Location = New-Object System.Drawing.Size(250, 14)
+ $DeviceSearchButton.Size = New-Object System.Drawing.Size(75, 22)
+ $DeviceSearchButton.Text = 'Device ID'
+ $DeviceSearchButton.Add_Click({ Get-UserDevices $UserTextBox.Text })
+ $Form.Controls.Add($DeviceSearchButton)
+
+ # Device ID
+ $DeviceIDTextBox = New-Object System.Windows.Forms.TextBox
+ $DeviceIDTextBox.Location = New-Object System.Drawing.Size(345, 15)
+ $DeviceIDTextBox.Size = New-Object System.Drawing.Size(125, 20)
+ $DeviceIDTextBox.ReadOnly = 'True'
+ $Form.Controls.Add($DeviceIDTextBox)
+
+ # Device ID
+ $DeviceNameLabel = New-Object System.Windows.Forms.Label
+ $DeviceNameLabel.Location = New-Object System.Drawing.Size(250, 48)
+ $DeviceNameLabel.Size = New-Object System.Drawing.Size(75, 20)
+ $DeviceNameLabel.Text = "Device Name"
+ $Form.Controls.Add($DeviceNameLabel)
+
+ # Device Name
+ $DeviceNameTextBox = New-Object System.Windows.Forms.TextBox
+ $DeviceNameTextBox.Location = New-Object System.Drawing.Size(345, 44)
+ $DeviceNameTextBox.Size = New-Object System.Drawing.Size(125, 20)
+ $DeviceNameTextBox.ReadOnly = 'True'
+ $Form.Controls.Add($DeviceNameTextBox)
+
+ # Retire button
+ $MigrateButton = New-Object System.Windows.Forms.Button
+ $MigrateButton.Location = New-Object System.Drawing.Size(50, 85)
+ $MigrateButton.Size = New-Object System.Drawing.Size(75, 22)
+ $MigrateButton.Text = 'Retire'
+ $MigrateButton.Add_Click({
+ try {
+ Invoke-CMDeviceRetire -Id $DeviceIDTextBox.Text -Force
+ Update-Log "Successfully retired $($DeviceNameTextBox.Text) [$($DeviceIDTextBox.Text)]." -Color Green
+ } catch {
+ Update-Log "There was a problem retiring $($DeviceNameTextBox.Text) [$($DeviceIDTextBox.Text)]:" -Color Red
+ Update-Log $_.Exception.Message -Color Red
+ }
+ })
+ $Form.Controls.Add($MigrateButton)
+
+ # Lock button
+ $LockButton = New-Object System.Windows.Forms.Button
+ $LockButton.Location = New-Object System.Drawing.Size(150, 85)
+ $LockButton.Size = New-Object System.Drawing.Size(75, 22)
+ $LockButton.Text = 'Lock'
+ $LockButton.Add_Click({
+ try {
+ Invoke-CMDeviceAction -Id $DeviceIDTextBox.Text -Action Lock -ErrorAction Stop
+
+ Update-Log "Lock on $($DeviceNameTextBox.Text) [$($DeviceIDTextBox.Text)] successfully initiated." -Color Green
+ } catch {
+ Update-Log "There was a problem performing Lock on $($DeviceNameTextBox.Text) [$($DeviceIDTextBox.Text)]:" -Color Red
+ Update-Log $_.Exception.Message -Color Red
+ }
+ })
+ $Form.Controls.Add($LockButton)
+
+ # Reset pin button
+ $ResetPinButton = New-Object System.Windows.Forms.Button
+ $ResetPinButton.Location = New-Object System.Drawing.Size(250, 85)
+ $ResetPinButton.Size = New-Object System.Drawing.Size(75, 22)
+ $ResetPinButton.Text = 'Reset Pin'
+ $ResetPinButton.Add_Click({
+ try {
+ Invoke-CMDeviceAction -Id $DeviceIDTextBox.Text -Action PinReset -ErrorAction Stop
+
+ Update-Log "Pin Reset on $($DeviceNameTextBox.Text) [$($DeviceIDTextBox.Text)] successfully initiated." -Color Green
+ } catch {
+ Update-Log "There was a problem performing Pin Reset on $($DeviceNameTextBox.Text) [$($DeviceIDTextBox.Text)]:" -Color Red
+ Update-Log $_.Exception.Message -Color Red
+ }
+ })
+ $Form.Controls.Add($ResetPinButton)
+
+ # Status button
+ $StatusButton = New-Object System.Windows.Forms.Button
+ $StatusButton.Location = New-Object System.Drawing.Size(350, 85)
+ $StatusButton.Size = New-Object System.Drawing.Size(75, 22)
+ $StatusButton.Text = 'Status'
+ $StatusButton.Add_Click({
+ try {
+ $Action = Get-CMDeviceAction -Id $DeviceIDTextBox.Text -Fast -ErrorAction Stop
+
+ if ($Action -ne $null) {
+ Update-Log "Getting action history for $($DeviceNameTextBox.Text) [$($DeviceIDTextBox.Text)]..."
+ Update-Log "Action`t`tState`t`tLast Update Time`tPin" -Color Gray
+
+ $Action | ForEach-Object {
+ Update-Log "$($_.Action)$(if ($_.Action -eq 'Lock') { "`t" })`t" -NoNewLine
+ Update-Log "$(switch ($_.State) { 1 { "Pending`t" } 4 { "Complete" } })`t" -NoNewLine
+ Update-Log "$($_.LastUpdateTime)`t" -NoNewLine
+ if ($_.Action -eq 'PinReset') {
+ $PinResetState = Get-WmiObject -ComputerName CMSERVER -NameSpace root/SMS/site_$(Get-PSDrive -PSProvider CMSite) `
+ -Class SMS_DeviceAction -Filter "Action='PinReset' and ResourceID='$($DeviceIDTextBox.Text)'" -ErrorAction Stop
+
+ $Pin = ([wmi]$PinResetState.__PATH).ResponseText
+ Update-Log $Pin
+ } else {
+ Update-Log 'N/A'
+ }
+ }
+ }
+ else {
+ Update-Log "No state information available for $($DeviceNameTextBox.Text) [$($DeviceIDTextBox.Text)]." -Color Yellow
+ }
+ } catch {
+ Update-Log "Failed to get the state information for $($DeviceNameTextBox.Text) [$($DeviceIDTextBox.Text)]:" -Color Red
+ Update-Log $_.Exception.Message -Color Red
+ }
+ })
+ $Form.Controls.Add($StatusButton)
+
+ # Report button
+ $ReportButton = New-Object System.Windows.Forms.Button
+ $ReportButton.Location = New-Object System.Drawing.Size(110, 44)
+ $ReportButton.Size = New-Object System.Drawing.Size(110, 22)
+ $ReportButton.Text = "Inactive Devices"
+ $ReportButton.Add_Click({ Get-InactiveDevices })
+ $Form.Controls.Add($ReportButton)
+
+ # Clear log button
+ $ClearButton = New-Object System.Windows.Forms.Button
+ $ClearButton.Location = New-Object System.Drawing.Size(12, 44)
+ $ClearButton.Size = New-Object System.Drawing.Size(75, 22)
+ $ClearButton.Text = "Clear Log"
+ $ClearButton.Add_Click({ $LogTextBox.Clear() })
+ $Form.Controls.Add($ClearButton)
+
+ Write-Title
+
+ $Form.Add_Shown({ $Form.Activate() })
+ $Form.ShowDialog()
+}
+
+try {
+ # Verify we have access to CM commands before we continue
+ Import-Module ConfigurationManager
+ Set-Location -Path "$(Get-PSDrive -PSProvider CMSite):\" -ErrorAction Stop
+
+ $CMPSSuppressFastNotUsedCheck = $true
+
+ New-UtilityForm
+} catch {
+ [System.Windows.Forms.MessageBox]::Show(
+ "Failed to set CM site drive. Are you running this from CMSERVER and is the console is up to date?
+ `n`nError: $($_.Exception.Message)",
+ 'Fail!'
+ )
+
+ exit
+}
\ No newline at end of file
diff --git a/SCCM/Retire-CMApplication.ps1 b/SCCM/Retire-CMApplication.ps1
index 9813701..b1ec9f5 100644
--- a/SCCM/Retire-CMApplication.ps1
+++ b/SCCM/Retire-CMApplication.ps1
@@ -2,26 +2,30 @@ function Retire-CMApplication {
[CmdletBinding()]
param (
[Parameter(Mandatory = $true, ValueFromPipeline = $true)]
- $RetiringApps = @()
+ $RetiringApps = @(),
+ [Parameter(Mandatory = $false)]
+ $rename = $false
)
# import cm module
- Import-Module '\\sccm01\SMS_Company\AdminConsole\bin\ConfigurationManager.psd1'
+ Import-Module ($Env:SMS_ADMIN_UI_PATH.Substring(0,$Env:SMS_ADMIN_UI_PATH.Length-5) + '\ConfigurationManager.psd1')
# change to the cm site drive
$PSD = Get-PSDrive -PSProvider CMSite
cd "$($PSD):"
# for each provided app name, remove deployments, rename, and retire
- foreach ($app in $RetiringApps) {
- if ($RetiringApp = Get-CMApplication -Name $app) {
- Write-Host "So long, $app!"
+ foreach ($RetiringAppName in $RetiringApps) {
+
+ if ($RetiringApp = Get-CMApplication -Name $RetiringAppName)
+ {
+ Write-Host "So long, $RetiringAppName!"
# checking retired status, setting to active so that we can make changes
- if ($RetiringApp.IsExpired) {
- $appWMI = gwmi -Namespace Root\SMS\Site_$PSD -class SMS_ApplicationLatest -Filter "LocalizedDisplayName = '$app'"
- $appWMI.SetIsExpired($false) | Out-Null
- Write-Host "Setting Status of $app to Active so that changes can be made."
+ if ($RetiringApp.IsExpired)
+ {
+ Resume-CMApplication -Name "$RetiringAppName"
+ Write-Host "Setting Status of $RetiringAppName to Active so that changes can be made."
}
$oldDeploys = Get-CMDeployment -SoftwareName $RetiringApp.LocalizedDisplayName
@@ -29,19 +33,26 @@ function Retire-CMApplication {
# remove all deployments for the app
if ($oldDeploys) {
$oldDeploys | ForEach-Object {
- Remove-CMDeployment -ApplicationName $app -DeploymentId $_.DeploymentID -Force
+ Remove-CMDeployment -ApplicationName $RetiringAppName -DeploymentId $_.DeploymentID -Force
}
- Write-Host "Removed $($oldDeploys.Count) deployments of $app."
+ Write-Host "Removed $($oldDeploys.Count) deployments of $RetiringApp."
}
# remove content from all dp's and dpg's
Write-Host -NoNewline "Removing content from all distribution points"
- $DPs = Get-CMDistributionPoint
- foreach ($DP in $DPs) {
+ $DPs = Get-CMDistributionPoint -AllSite
+ foreach ($DP in $DPs)
+ {
+ $dpName = ($dp.NetworkOSPath).Substring(2)
+
+
+ Write-Verbose "Removing $RetiringAppName from $dpName"
Write-Host -NoNewline "."
- try {
- Remove-CMContentDistribution -Application $RetiringApp -DistributionPointName ($DP).NetworkOSPath -Force -EA SilentlyContinue
- } catch { }
+ try
+ {
+ Remove-CMContentDistribution -ApplicationName "$RetiringAppName" -DistributionPointName $dpName -Force -EA SilentlyContinue #TODO: parallelize this
+ }
+ catch { }
}
Write-Host
Write-Host -NoNewline "Removing content from all distribution point groups"
@@ -49,33 +60,37 @@ function Retire-CMApplication {
foreach ($DPG in $DPGs) {
Write-Host -NoNewline "."
try {
- Remove-CMContentDistribution -Application $RetiringApp -DistributionPointGroupName ($DPG).Name -Force -EA SilentlyContinue
+ Remove-CMContentDistribution -ApplicationName "$RetiringAppName" -DistributionPointGroupName ($DPG).Name -Force -EA SilentlyContinue #TODO: parallelize this
} catch { }
}
Write-Host
- # rename the app
- $app = $app.Replace('Retired-', '')
- try {
- Set-CMApplication -Name $app -NewName "Retired-$app"
- } catch { }
- Write-Host "Renamed to Retired-$app."
+ If ($rename){
+ # rename the app
+ $RetiringAppName = $RetiringApp.Replace('Retired-', '')
+ try {
+ Set-CMApplication -Name $RetiringAppName -NewName "Retired-$RetiringApp"
+ } catch { }
+ Write-Host "Renamed to Retired-$RetiringAppName."
- # move the app according to category
- if ($RetiringApp.LocalizedCategoryInstanceNames -eq "Mac") {
- Move-CMObject -FolderPath "Application\Retired" -InputObject $RetiringApp
- Write-Host "Moved to Retired."
- } else {
- Move-CMObject -FolderPath "Application\Retired" -InputObject $RetiringApp
- Write-Host "Moved to Retired."
+ # move the app according to category
+ if ($RetiringApp.LocalizedCategoryInstanceNames -eq "Mac") {
+ Move-CMObject -FolderPath "Application\Retired" -InputObject $RetiringApp
+ Write-Host "Moved to Retired."
+ } else {
+ Move-CMObject -FolderPath "Application\Retired" -InputObject $RetiringApp
+ Write-Host "Moved to Retired."
+ }
}
-
+
# retire the app
- if (!$RetiringApp.IsExpired) {
- $appWMI = gwmi -Namespace Root\SMS\Site_$PSD -class SMS_ApplicationLatest -Filter "LocalizedDisplayName = 'Retired-$app'"
- $appWMI.SetIsExpired($true) | Out-Null
+ if (!$RetiringApp.IsExpired)
+ {
+ Suspend-CMApplication -Name "$RetiringAppName"
Write-Host "Set status to Retired."
- } else {
+ }
+ else
+ {
Write-Host "Status was already set to Retired."
}
@@ -85,7 +100,7 @@ function Retire-CMApplication {
Write-Host "Don't forget to delete the source files from $loc."
} else {
- Write-Host "$app was not found. No actions performed."
+ Write-Host "$RetiringAppName was not found. No actions performed."
}
}
}
diff --git a/SCCM/Retire-CMApplicationGUI.ps1 b/SCCM/Retire-CMApplicationGUI.ps1
index c8f7a2a..59d0cfb 100644
--- a/SCCM/Retire-CMApplicationGUI.ps1
+++ b/SCCM/Retire-CMApplicationGUI.ps1
@@ -140,7 +140,7 @@ function Create-UtilityForm {
try {
# make sure we have access to CM commands before we continue
- Import-Module 'E:\SCCM\AdminConsole\bin\ConfigurationManager.psd1'
+ Import-Module ($Env:SMS_ADMIN_UI_PATH.Substring(0,$Env:SMS_ADMIN_UI_PATH.Length-5) + '\ConfigurationManager.psd1')
Set-Location -Path "$(Get-PSDrive -PSProvider CMSite):\" -ErrorAction Stop
Create-UtilityForm
} catch {
diff --git a/Send-LinuxCommand.ps1 b/Send-LinuxCommand.ps1
new file mode 100644
index 0000000..0678d04
--- /dev/null
+++ b/Send-LinuxCommand.ps1
@@ -0,0 +1,81 @@
+[CmdletBinding(SupportsShouldProcess = $true)]
+Param(
+ [Parameter(Mandatory = $true)]
+ [ValidateNotNullOrEmpty()]
+ [ValidateSet(
+ 'Linux1', 'Linux2', 'All'
+ )]
+ [string[]]$Server,
+ [switch]$Sudo,
+ [switch]$Update,
+ [switch]$Upgrade,
+ [switch]$DistUpgrade,
+ [switch]$Clean,
+ [string[]]$Install,
+ [string]$Command,
+ [switch]$Reboot,
+ [switch]$AsJob,
+ [Parameter(Mandatory = $true)]
+ [ValidateNotNullOrEmpty()]
+ [PSCredential]$Credential
+)
+
+$PLinkPath = '\\192.168.1.4\data\Programs\plink.exe'
+if (-not (Test-Path -Path $PLinkPath)) {
+ Write-Warning "$PLinkPath not found, downloading..."
+ try {
+ Invoke-WebRequest -Uri 'https://the.earth.li/~sgtatham/putty/latest/w64/plink.exe' -OutFile $PLinkPath
+ } catch {
+ Write-Warning "Unable to download PLink."
+ Pause
+ exit
+ }
+}
+
+$UserName = $Creds.GetNetworkCredential().UserName
+$Password = $Creds.GetNetworkCredential().Password
+
+$ServerList = @{
+ 'Linux1' = '192.168.1.2';
+ 'Linux2' = '192.168.1.3';
+}
+
+$Commands = @("hostname")
+if ($Command) { $Commands += $Command }
+if ($Sudo) { $Commands += "echo $Password | sudo -S true;" }
+if ($Update) { $Commands += "sudo apt -y update;" }
+if ($Upgrade) { $Commands += "sudo apt -y upgrade;" }
+if ($DistUpgrade) { $Commands += "sudo apt -y dist-upgrade;" }
+if ($Clean) { $Commands += @("sudo apt -y autoremove;", "sudo apt -y autoclean;") }
+if ($Install) { $Commands += "sudo apt -y install $Install;" }
+if ($Reboot) { $Commands += "sudo shutdown -r;" }
+
+# Make sure the end of each line contains a semicolon
+$Commands = $Commands | ForEach-Object { "$($_.ToString().TrimEnd(';'));" }
+
+if ($Server -eq 'All') { $Server = $ServerList.Keys }
+
+foreach ($ComputerName in $Server) {
+ if ($PSCmdlet.ShouldProcess($Server, "Run the following commands:`n$(($Commands | Out-String) -replace $Password, '***')")) {
+ if ($AsJob) {
+ Write-Verbose "Running commands as job..."
+ Start-Job -Name "$ComputerName Update" -ScriptBlock {
+ param($IP, $UserName, $Password, $Commands, $PLinkPath)
+
+ Set-Alias plink $PLinkPath
+ Write-Output 'y' | plink -ssh $ServerList.$ComputerName -l $Username -pw $Password exit
+ plink -batch -ssh $ServerList.$ComputerName -l $UserName -pw $Password $Commands
+ } -ArgumentList $ServerList.$ComputerName, $UserName, $Password, $Commands, $PLinkPath
+ } else {
+ Set-Alias plink $PLinkPath
+ Write-Output 'y' | plink -ssh $ServerList.$ComputerName -l $Username -pw $Password exit
+ plink -batch -ssh $ServerList.$ComputerName -l $UserName -pw $Password $Commands
+ }
+ }
+}
+
+if ($AsJob) {
+ Write-Verbose "Waiting for jobs to finish..."
+ Get-Job | Wait-Job | Receive-Job
+ Get-Job | Remove-Job -Force
+}
\ No newline at end of file
diff --git a/Set-ScriptRoot.ps1 b/Set-ScriptRoot.ps1
new file mode 100644
index 0000000..9e2a4b1
--- /dev/null
+++ b/Set-ScriptRoot.ps1
@@ -0,0 +1,6 @@
+# Set ScripRoot variable to the path which the script is executed from
+$ScriptRoot = if ($PSVersionTable.PSVersion.Major -lt 3) {
+ Split-Path -Path $MyInvocation.MyCommand.Path
+} else {
+ $PSScriptRoot
+}
\ No newline at end of file
diff --git a/Test-IsAdmin.ps1 b/Test-IsAdmin.ps1
new file mode 100644
index 0000000..a035cd9
--- /dev/null
+++ b/Test-IsAdmin.ps1
@@ -0,0 +1,8 @@
+function Test-IsAdmin {
+ $UserIdentity = [Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()
+
+ if (-not $UserIdentity.IsInRole([Security.Principal.WindowsBuiltInRole] 'Administrator')) {
+ Update-Log "You are not running this script with an admin account. " -Color 'Red' -NoNewLine
+ Update-Log "Some tasks may fail if not run with admin credentials.`n" -Color 'Red'
+ }
+}
\ No newline at end of file
diff --git a/Test-IsIse.ps1 b/Test-IsIse.ps1
new file mode 100644
index 0000000..c715da5
--- /dev/null
+++ b/Test-IsIse.ps1
@@ -0,0 +1,2 @@
+# Return $true if run from ISE
+function Test-IsISE { if ($psISE) { $true } else { $false } }
\ No newline at end of file
diff --git a/Validate-CSVHeaders.ps1 b/Validate-CSVHeaders.ps1
index 35b4b09..ae3ae84 100644
--- a/Validate-CSVHeaders.ps1
+++ b/Validate-CSVHeaders.ps1
@@ -8,7 +8,7 @@ function Validate-CSVHeaders ($correctHeaders) {
[ValidateSet('Yes','No')]$validateHeaders = Read-Host "Validate headers?"
if ($validateHeaders -eq 'Yes') {
# put all the headers into a comma separated array
- $headers = (Get-Content $fileName | Select-Object -First 1).Split(",")
+ $headers = (Get-Content $fileName -TotalCount 1).Split(",")
for ($i = 0; $i -lt $headers.Count; $i++) {
# trim any leading white space and compare the headers
@@ -21,4 +21,4 @@ function Validate-CSVHeaders ($correctHeaders) {
}
}
-Validate-CSVHeaders $correctHeaders
\ No newline at end of file
+Validate-CSVHeaders $correctHeaders
diff --git a/Windows Updates/Get-InstalledUpdates.ps1 b/Windows Updates/Get-InstalledUpdates.ps1
new file mode 100644
index 0000000..80e3855
--- /dev/null
+++ b/Windows Updates/Get-InstalledUpdates.ps1
@@ -0,0 +1,20 @@
+Function Get-InstalledUpdates($title) {
+ $UpdateSession = New-Object -ComObject Microsoft.Update.Session
+ $SearchResult = $null
+ $UpdateSearcher = $UpdateSession.CreateUpdateSearcher()
+ $UpdateSearcher.Online = $true
+ $SearchResult = $UpdateSearcher.Search("IsInstalled=1 and Type='Software'")
+
+ $i = 1
+ foreach($Update in $SearchResult.Updates)
+ {
+ If ($Update.Title -like "*$title*") {
+ Write-Host "$i) $($Update.Title + " | " + $Update.SecurityBulletinIDs)"
+ $i += 1
+ }
+ }
+}
+
+cls
+
+Get-InstalledUpdates("Lync")
\ No newline at end of file
diff --git a/Windows Updates/Get-MicrosoftUpdates.ps1 b/Windows Updates/Get-MicrosoftUpdates.ps1
new file mode 100644
index 0000000..f32fb0f
Binary files /dev/null and b/Windows Updates/Get-MicrosoftUpdates.ps1 differ
diff --git a/Enable-BitLocker.ps1 b/Windows/Enable-BitLocker.ps1
similarity index 97%
rename from Enable-BitLocker.ps1
rename to Windows/Enable-BitLocker.ps1
index c62ed5d..99182b5 100644
--- a/Enable-BitLocker.ps1
+++ b/Windows/Enable-BitLocker.ps1
@@ -1,71 +1,71 @@
-$Creds = Get-Credential
-
-$Computers = @(
- 'computer1', 'computer2'
-)
-
-# Log file
-$Date = (Get-Date).ToString('yyyyMMdd-HHmm')
-$LogFolder = New-Item -ItemType Directory "C:\Logs" -Force
-$Log = New-Item -ItemType File "$LogFolder\Enable-BitLocker-$Date.log" -Force
-
-foreach ($Computer in $Computers) {
-
- Add-Content $Log "$Computer..."
-
- if (Test-Connection $Computer -Quiet -Count 3) {
-
- $Return = Invoke-Command -ComputerName $Computer -Credential $Creds -ScriptBlock {
-
- # Delete any current BitLocker pin
- try {
- $Result = ((manage-bde -protectors -delete C: | Select-String -Pattern "(?<=(ERROR:)).*").ToString()).TrimStart()
- $Return = 'Deleted old protectors.'
- } catch {
- $Return += $Error
- }
- $Return += "`n"
-
- # Add new Pin and TPM security
- try {
- $Result = ((manage-bde -protectors -add C: -tp newpin | Select-String -Pattern "(?<=(ERROR:)).*").ToString()).TrimStart()
- $Return += 'Added new protectors.'
- } catch {
- $Return += $Error
- }
- $Return += "`n"
-
- # Get the BitLocker's status after changes
- $Result = ((manage-bde -status | Select-String -Pattern "(?<=(Protection Status:)).*").ToString()).Replace('Protection Status:', '').TrimStart()
- if (!$Result) {
- $Return += 'ERROR: Unable to capture BitLocker status.'
- } elseif ($Result -eq 'Protection Off') {
- # Enable protection
- try {
- $Result = ((manage-bde -protectors -enable C: | Select-String -Pattern "(?<=(ERROR:)).*").ToString()).TrimStart()
- $Return += 'Enabled protectors.'
- } catch {
- $Return += $Error
- }
- $Return += "`n"
-
- # Get the BitLocker's status after changes
- $Result = ((manage-bde -status | Select-String -Pattern "(?<=(Protection Status:)).*").ToString()).Replace('Protection Status:', '').TrimStart()
- if ($Result -eq 'Protection Off') {
- $Return += 'ERROR: Unable to enable protectors.'
- } elseif ($Result -eq 'Protection On') {
- $Return += $Result
- }
- } elseif ($Result -eq 'Protection On') {
- $Return += $Result
- }
-
- Return $Return
- }
-
- Add-Content $Log $Return
-
- } else {
- Add-Content $Log "ERROR: Failed to connect."
- }
+$Creds = Get-Credential
+
+$Computers = @(
+ 'computer1', 'computer2'
+)
+
+# Log file
+$Date = (Get-Date).ToString('yyyyMMdd-HHmm')
+$LogFolder = New-Item -ItemType Directory "C:\Logs" -Force
+$Log = New-Item -ItemType File "$LogFolder\Enable-BitLocker-$Date.log" -Force
+
+foreach ($Computer in $Computers) {
+
+ Add-Content $Log "$Computer..."
+
+ if (Test-Connection $Computer -Quiet -Count 3) {
+
+ $Return = Invoke-Command -ComputerName $Computer -Credential $Creds -ScriptBlock {
+
+ # Delete any current BitLocker pin
+ try {
+ $Result = ((manage-bde -protectors -delete C: | Select-String -Pattern "(?<=(ERROR:)).*").ToString()).TrimStart()
+ $Return = 'Deleted old protectors.'
+ } catch {
+ $Return += $Error
+ }
+ $Return += "`n"
+
+ # Add new Pin and TPM security
+ try {
+ $Result = ((manage-bde -protectors -add C: -tp newpin | Select-String -Pattern "(?<=(ERROR:)).*").ToString()).TrimStart()
+ $Return += 'Added new protectors.'
+ } catch {
+ $Return += $Error
+ }
+ $Return += "`n"
+
+ # Get the BitLocker's status after changes
+ $Result = ((manage-bde -status | Select-String -Pattern "(?<=(Protection Status:)).*").ToString()).Replace('Protection Status:', '').TrimStart()
+ if (!$Result) {
+ $Return += 'ERROR: Unable to capture BitLocker status.'
+ } elseif ($Result -eq 'Protection Off') {
+ # Enable protection
+ try {
+ $Result = ((manage-bde -protectors -enable C: | Select-String -Pattern "(?<=(ERROR:)).*").ToString()).TrimStart()
+ $Return += 'Enabled protectors.'
+ } catch {
+ $Return += $Error
+ }
+ $Return += "`n"
+
+ # Get the BitLocker's status after changes
+ $Result = ((manage-bde -status | Select-String -Pattern "(?<=(Protection Status:)).*").ToString()).Replace('Protection Status:', '').TrimStart()
+ if ($Result -eq 'Protection Off') {
+ $Return += 'ERROR: Unable to enable protectors.'
+ } elseif ($Result -eq 'Protection On') {
+ $Return += $Result
+ }
+ } elseif ($Result -eq 'Protection On') {
+ $Return += $Result
+ }
+
+ Return $Return
+ }
+
+ Add-Content $Log $Return
+
+ } else {
+ Add-Content $Log "ERROR: Failed to connect."
+ }
}
\ No newline at end of file
diff --git a/Windows/Enable-Ping.ps1 b/Windows/Enable-Ping.ps1
new file mode 100644
index 0000000..d81b88b
--- /dev/null
+++ b/Windows/Enable-Ping.ps1
@@ -0,0 +1,3 @@
+Invoke-Command -ComputerName (Get-ADComputer -Filter *).DNSHostName -ScriptBlock {
+ New-NetFirewallRule –DisplayName “Allow Ping” –Direction Inbound –Action Allow –Protocol icmpv4 –Enabled True
+}
\ No newline at end of file
diff --git a/Windows/Enable-RDP.ps1 b/Windows/Enable-RDP.ps1
new file mode 100644
index 0000000..a0dcec8
--- /dev/null
+++ b/Windows/Enable-RDP.ps1
@@ -0,0 +1,3 @@
+Set-ItemProperty -Path 'HKLM:\System\CurrentControlSet\Control\Terminal Server'-name "fDenyTSConnections" -Value 0
+Enable-NetFirewallRule -DisplayGroup "Remote Desktop"
+Set-ItemProperty -Path 'HKLM:\System\CurrentControlSet\Control\Terminal Server\WinStations\RDP-Tcp' -name "UserAuthentication" -Value 1
\ No newline at end of file
diff --git a/Windows/Get-BackupStatus.ps1 b/Windows/Get-BackupStatus.ps1
new file mode 100644
index 0000000..a358f5b
--- /dev/null
+++ b/Windows/Get-BackupStatus.ps1
@@ -0,0 +1,15 @@
+if ($env:COMPUTERNAME -ne 'BackupServer') {
+ Write-Warning 'This needs to be run from [BackupServer]'
+ Pause
+ exit
+}
+
+Get-WBJob
+
+while ((Get-WBJob).JobState -eq 'Running') {
+ (Get-WBJob).CurrentOperation
+ Start-Sleep -Seconds 30
+}
+
+Get-WBJob
+Pause
\ No newline at end of file
diff --git a/Windows/Get-ComputerNetInfo.ps1 b/Windows/Get-ComputerNetInfo.ps1
new file mode 100644
index 0000000..eed3128
--- /dev/null
+++ b/Windows/Get-ComputerNetInfo.ps1
@@ -0,0 +1,31 @@
+function Get-ComputerNetInfo {
+ param (
+ [Parameter(
+ ValueFromPipeline = $true,
+ ValueFromPipelineByPropertyName = $true
+ )]
+ [string[]]$ComputerName = $env:COMPUTERNAME
+ )
+
+ process {
+ foreach ($Computer in $ComputerName ) {
+ # Get hostname and IP address from DNS provider
+ Write-Verbose "Getting DNS providor info for $Computer..."
+ $DNSHostEntry = [System.Net.Dns]::GetHostEntry($Computer)
+ $HostName = $DNSHostEntry.HostName
+ $IPAddress = $DNSHostEntry.AddressList.IPAddressToString.Split('.', 1)[0]
+
+ # Get MAC from WMI class
+ Write-Verbose "Getting WMI providor info for $Computer..."
+ $NetAdapter = Get-WmiObject -Class Win32_NetworkAdapterConfiguration -ComputerName $Computer
+ $MACAddress = ($NetAdapter | Where-Object {$_.IpAddress -eq $IPAddress}).MACAddress
+
+ # Create and output a custom psobject with the net info
+ $NetInfo = New-Object -TypeName psobject
+ $NetInfo | Add-Member -MemberType NoteProperty -Name DNSHostName -Value $HostName
+ $NetInfo | Add-Member -MemberType NoteProperty -Name IPAddress -Value $IPAddress
+ $NetInfo | Add-Member -MemberType NoteProperty -Name MACAddress -Value $MACAddress
+ Write-Output $NetInfo
+ }
+ }
+}
\ No newline at end of file
diff --git a/Windows/Get-DetailsofGroupsUserMemberOf.ps1 b/Windows/Get-DetailsofGroupsUserMemberOf.ps1
new file mode 100644
index 0000000..5f34a37
--- /dev/null
+++ b/Windows/Get-DetailsofGroupsUserMemberOf.ps1
@@ -0,0 +1,3 @@
+#Powershell one line script to get details of all groups user memberof
+
+Get-ADPrincipalGroupMembership ashishanand |Get-ADGroup -Properties * | select name, managedby, *
diff --git a/Windows/Get-DiskInfo.ps1 b/Windows/Get-DiskInfo.ps1
new file mode 100644
index 0000000..c57b1f0
--- /dev/null
+++ b/Windows/Get-DiskInfo.ps1
@@ -0,0 +1,69 @@
+<#
+.Synopsis
+ Get info on each partition and logical volume of a machine.
+
+#>
+[CmdletBinding()]
+param(
+ [Parameter(
+ ValueFromPipeline=$true,
+ ValueFromPipelineByPropertyName=$true,
+ Position = 0
+ )]
+ [string[]]$ComputerName = $env:COMPUTERNAME,
+
+ [pscredential]$Credential
+)
+
+foreach ($Computer in $ComputerName) {
+ try {
+ $WmiObjectParams = @{
+ ComputerName = $Computer
+ Credential = $creds
+ Class = 'Win32_DiskPartition'
+ Property = 'Name, DiskIndex, Type'
+ }
+ $Disks = Get-WmiObject @WmiObjectParams -ErrorAction Stop |
+ Select-Object -Property Name, DiskIndex, @{
+ Name="GPT"
+ Expression = { $_.Type.StartsWith("GPT") }
+ }
+ } catch {
+ New-Object -TypeName psobject -Property @{
+ ComputerName = $Computer
+ Error = $_.Exception.Message
+ VolumeName = ''
+ DiskName = ''
+ GPT = ''
+ DiskIndex = ''
+ DriveLetter = ''
+ FreeSpaceGB = ''
+ Utilization = ''
+ SizeGB = ''
+ FileSystem = ''
+ }
+ continue
+ }
+
+ Get-WmiObject Win32_LogicalDisk -ComputerName $Computer -Credential $creds |
+ Where-Object -Property DriveType -EQ 3 | Foreach-Object {
+ $Query = "Associators of {Win32_LogicalDisk.DeviceID='$($_.DeviceID)'} WHERE ResultRole=Antecedent"
+ $Volume = Get-WmiObject -ComputerName $Computer -Credential $creds -Query $Query
+
+ $Disk = $Disks | Where-Object -Property DiskIndex -EQ $Volume.DiskIndex
+
+ New-Object -TypeName psobject -Property @{
+ ComputerName = $Computer
+ Error = ''
+ VolumeName = $_.VolumeName
+ DiskName = $Disk.Name -join ', '
+ GPT = $Disk.GPT -join ', '
+ DiskIndex = $Volume.DiskIndex
+ DriveLetter = $_.DeviceID
+ FreeSpaceGB = [Math]::Round($_.FreeSpace / 1GB, 2)
+ Utilization = (1 - (($_.FreeSpace / 1GB) / ($Volume.Size / 1GB))).ToString('P')
+ SizeGB = [Math]::Round($Volume.Size / 1GB, 2)
+ FileSystem = $_.FileSystem
+ }
+ }
+}
\ No newline at end of file
diff --git a/Windows/Get-NTPTimeOffset.ps1 b/Windows/Get-NTPTimeOffset.ps1
new file mode 100644
index 0000000..2b6bd7d
--- /dev/null
+++ b/Windows/Get-NTPTimeOffset.ps1
@@ -0,0 +1,80 @@
+$Times = @{}
+$Count = 0
+$CountLimit = 60
+$Server = 'server05'
+$ServerToCompare = 'server06'
+
+function Get-TimeOffset {
+ $ResultObject = New-Object -TypeName psobject
+ Add-Member -InputObject $ResultObject NoteProperty Computer 'pool.ntp.org'
+ $W32TMResult = w32tm /stripchart /computer:'pool.ntp.org' /dataonly /samples:3 /ipprotocol:4
+
+ if (-not ($W32TMResult -is [array])) {
+ Add-Member -InputObject $ResultObject NoteProperty Status "Offline"
+ Add-Member -InputObject $ResultObject NoteProperty Offset $null
+ } else {
+ $FoundTime = $false
+
+ # Go through the 5 samples to find a response with timeoffset
+ for ($i = 3; $i -lt 8; $i++) {
+ if (-not $FoundTime) {
+ if ($W32TMResult[$i] -match ", ([-+]\d+\.\d+)s") {
+ $Offset = [float]$Matches[1]
+ Add-Member -InputObject $ResultObject NoteProperty Status "Online"
+ Add-Member -InputObject $ResultObject NoteProperty Offset $Offset
+ $FoundTime = $true
+ }
+ }
+ }
+
+ # If no time samples were found check for error
+ if (-not $FoundTime) {
+ if ($W32TMResult[3] -match "error") {
+ #0x800705B4 is not advertising/responding
+ Add-Member -InputObject $ResultObject NoteProperty Status "NTP not responding"
+ } else {
+ Add-Member -InputObject $ResultObject NoteProperty Status $W32TMResult[3]
+ }
+ Add-Member -InputObject $ResultObject NoteProperty Offset $null
+ }
+ }
+
+ $ResultObject
+}
+
+while ($true) {
+ $Count++
+
+ if (Test-Connection -Quiet -ComputerName $Server) {
+ $Offset = try {
+ $Result = Invoke-Command -ComputerName $Server -ScriptBlock ${function:Get-TimeOffset}
+ try { $Result.Offset } catch { $Result.Status }
+ } catch {
+ $_ | Out-String
+ }
+
+ $Times.Add((Get-Date), $Offset)
+ Start-Sleep -Seconds 10
+ } else {
+ $Times.Add((Get-Date), 'Unreachable')
+ Start-Sleep -Seconds 10
+ }
+
+ if ($Count -ge $CountLimit) {
+ $OffsetToCompare = try {
+ $Result = Invoke-Command -ComputerName $Server -ScriptBlock ${function:Get-TimeOffset}
+ try { $Result.Offset } catch { $Result.Status }
+ } catch {
+ $_ | Out-String
+ }
+
+ $SortedTimesString = $Times.GetEnumerator() | Sort-Object -Property Name | Out-String
+
+ Send-MailMessage -Body "$ServerToCompare offset = $OffsetToCompare`n`n$Server datestamp and offset:`n$SortedTimesString" `
+ -Subject 'server05 time script' -From 'Alerts@company.com' -To 'me@company.com' -SmtpServer smtp.company.local
+
+ Write-Host 'Reset the clocks...'
+ $Count = 0
+ $Times = @{}
+ }
+}
\ No newline at end of file
diff --git a/Windows/Get-NetSessionInfoString.ps1 b/Windows/Get-NetSessionInfoString.ps1
new file mode 100644
index 0000000..1c65c72
--- /dev/null
+++ b/Windows/Get-NetSessionInfoString.ps1
@@ -0,0 +1,13 @@
+$Creds = Get-Credential
+
+$Servers = (Get-ADGroup -Identity 'Domain Controllers' | Get-ADGroupMember).Name
+
+foreach ($Server in $Servers) {
+ $Server
+
+ (Invoke-Command -ComputerName $Server -Credential $Creds -ScriptBlock {
+ $key = "HKLM:\SYSTEM\CurrentControlSet\Services\LanmanServer\DefaultSecurity"
+ $name = "SrvsvcSessionInfo"
+ (Get-ItemProperty -Path $key -Name $name).SrvsvcSessionInfo
+ }) -join ''
+}
\ No newline at end of file
diff --git a/Windows/Get-ServerLoad.ps1 b/Windows/Get-ServerLoad.ps1
new file mode 100644
index 0000000..bc86f69
--- /dev/null
+++ b/Windows/Get-ServerLoad.ps1
@@ -0,0 +1,26 @@
+[CmdletBinding(SupportsShouldProcess = $true)]
+Param(
+ [Parameter(
+ Mandatory = $true,
+ ValueFromPipeline = $true,
+ ValueFromPipelineByPropertyName = $true
+ )]
+ [string[]]$ComputerName
+)
+
+foreach ($Computer in $ComputerName) {
+ $CPULoad = Get-WmiObject -Class win32_processor -ComputerName $Computer |
+ Measure-Object -Property LoadPercentage -Average | Select-Object Average
+
+ $MemLoad = Get-WmiObject -Class win32_operatingsystem -ComputerName $Computer |
+ Select-Object @{
+ Name = "MemoryUsage"
+ Expression = { "{0:N2}" -f ((($_.TotalVisibleMemorySize - $_.FreePhysicalMemory) * 100) / $_.TotalVisibleMemorySize) }
+ }
+
+ New-Object -TypeName PSObject -Property @{
+ ComputerName = $Computer
+ CPUUsage = $CPULoad.Average
+ MemoryUsage = $MemLoad.MemoryUsage
+ }
+}
\ No newline at end of file
diff --git a/Windows/Install-RemovedWindowsFeatures.ps1 b/Windows/Install-RemovedWindowsFeatures.ps1
new file mode 100644
index 0000000..833cb0b
--- /dev/null
+++ b/Windows/Install-RemovedWindowsFeatures.ps1
@@ -0,0 +1,5 @@
+# Get server index numbers
+dism /get-wiminfo /wimfile:d:\sources\install.wim
+
+# Change feature name and final index number
+Install-WindowsFeature -Name RDS-Gateway -Source wim:D:\sources\install.wim:1
\ No newline at end of file
diff --git a/Windows/Set-TimeZoneEST.ps1 b/Windows/Set-TimeZoneEST.ps1
new file mode 100644
index 0000000..a7ee2f4
--- /dev/null
+++ b/Windows/Set-TimeZoneEST.ps1
@@ -0,0 +1 @@
+Get-ADComputer -Filter * | Invoke-Command -ScriptBlock { tzutil /s "Eastern Standard Time" }
\ No newline at end of file
diff --git a/Windows/Test-Is64BitOs.ps1 b/Windows/Test-Is64BitOs.ps1
new file mode 100644
index 0000000..d5cbe00
--- /dev/null
+++ b/Windows/Test-Is64BitOs.ps1
@@ -0,0 +1,8 @@
+# Return $true if 64-bit OS
+function Test-64BitOS {
+ if ((Get-WmiObject Win32_OperatingSystem).OSArchitecture -eq '64-bit') {
+ $true
+ } else {
+ $false
+ }
+}
\ No newline at end of file
diff --git a/Windows/Test-WebPorts.ps1 b/Windows/Test-WebPorts.ps1
new file mode 100644
index 0000000..7aa5ddf
--- /dev/null
+++ b/Windows/Test-WebPorts.ps1
@@ -0,0 +1,6 @@
+$Servers = @('server02' ,'server08')
+$Servers | ForEach-Object {
+ Write-Host $_
+ Test-NetConnection -ComputerName $_ -Port 80 -InformationLevel Quiet
+ Test-NetConnection -ComputerName $_ -Port 443 -InformationLevel Quiet
+}
\ No newline at end of file
diff --git a/Windows/UploadUserPhotoinAD.ps1 b/Windows/UploadUserPhotoinAD.ps1
new file mode 100644
index 0000000..4c81d8b
--- /dev/null
+++ b/Windows/UploadUserPhotoinAD.ps1
@@ -0,0 +1 @@
+Set-UserPhoto "" -PictureData ([System.IO.File]::ReadAllBytes(""))
diff --git a/Write-CMTraceLog.ps1 b/Write-CMTraceLog.ps1
new file mode 100644
index 0000000..c896aa6
--- /dev/null
+++ b/Write-CMTraceLog.ps1
@@ -0,0 +1,47 @@
+function Write-CMTraceLog {
+ <#
+ .Description
+ Write to a cmtrace readable log.
+ .Example
+ $LogFile = "C:\TestFolder\TestLog.Log"
+ Write-CMTraceLog -LogFile $LogFile
+ Write-CMTraceLog -Message "This is a normal message" -ErrorMessage $Error -LogFile $LogFile
+ Write-CMTraceLog -Message "This is a warning" -Type 2 -Component "Test Component" -LogFile $LogFile
+ Write-CMTraceLog -Message "This is an Error!" -Type 3 -Component "Error Component" -LogFile $LogFile
+ #>
+ param (
+ [Parameter(Mandatory = $false)]
+ [string]$Message,
+
+ [Parameter(Mandatory = $false)]
+ [string]$ErrorMessage,
+
+ [Parameter(Mandatory = $false)]
+ [string]$Component,
+
+ # 1 = Normal, 2 = Warning (yellow), 3 = Error (red)
+ [Parameter(Mandatory = $false)]
+ [int]$Type,
+
+ [Parameter(Mandatory = $true)]
+ [string]$LogFile
+ )
+
+ $Time = Get-Date -Format "HH:mm:ss.ffffff"
+ $Date = Get-Date -Format "MM-dd-yyyy"
+
+ if ($ErrorMessage -ne $null) {
+ $Type = 3
+ }
+ if ($Component -eq $null) {
+ $Component = " "
+ }
+ if ($Type -eq $null) {
+ $Type = 1
+ }
+
+ $LogMessage = "