by David | Aug 1, 2021 | Information Technology, PowerShell
One of the things I love to do is add a Dad joke to my reports. Reddit has some good ones. What’s cool about Reddit is they have a JSON backend that can be used and Used I do.
The Function
Function Get-DadJoke {
$DadJoke = Invoke-RestMethod -Uri "https://www.reddit.com/r/dadjokes/top.json" -UseBasicParsing
$Joke = $DadJoke.data.children[(Get-Random -Minimum 0 -Maximum $($dadjoke.data.children.count - 1))]
Write-Host "$($Joke.data.title)"
Write-Host "$($Joke.data.selftext)"
}
The Breakdown
This script is super simple. We are using a rest method to grab the JSON information. Wrapping it in a function with a write-host. Nothing more simple.
$DadJoke = Invoke-RestMethod -Uri "https://www.reddit.com/r/dadjokes/top.json" -UseBasicParsing
The first part is the dad jokes themselves. We are grabbing the top jokes on the subreddit. We use the Invoke-RestMethod because we are grabbing that JSON.
$Joke = $DadJoke.data.children[(Get-Random -Minimum 0 -Maximum $($dadjoke.data.children.count - 1))]
The next line grabs a random Joke from the list. The $DadJoke.Data.Children are an array. We are grabbing a random index from the array where the minimum is 0. The maximum is the number of arrays minus one. We do a minus one because everything starts at 0.
Write-Host "$($Joke.data.title)"
Write-Host "$($Joke.data.selftext)"
Finally, we write-host out the information. Notice once again, we use the $() structure. This way we can grab the subarrays of each item and displays the information accordingly.
A very simple breakdown of the function, and I hope you all enjoyed it.
by David | Jul 30, 2021 | Deployments, Information Technology, PowerShell, Resources
My last weekly challenge to myself was to deploy google chrome with Powershell. This one was the hardest because it was hard to find the latest and greatest MSI 64 bit of google chrome. The coding wasn’t too hard after that. How google chrome works, follows suit the same way as firefox did.
The Script
$ChromeSource = "http://dl.google.com/tag/s/defaultbrowser/edgedl/chrome/install/GoogleChromeStandaloneEnterprise64.msi"
$Installer = "$ENV:TEMP\Google.msi"
Invoke-WebRequest -Uri $ChromeSource -OutFile $Installer
Get-Process -Name "Chrome" | Stop-Process -Force
msiexec /i $Installer /qn /norestart
Remove-Item $Installer
The Breakdown
We first start off getting the latest enterprise edition of google chrome. This is the 64-bit version thus the most stable version on them all. This is the version I would install everywhere in a company if I had a choice.
$ChromeSource = "http://dl.google.com/tag/s/defaultbrowser/edgedl/chrome/install/GoogleChromeStandaloneEnterprise64.msi"
Then we create the installer path and download the file using the Invoke-webrequest with the Outfile as the installer path.
$Installer = "$ENV:TEMP\Google.msi"
Invoke-WebRequest -Uri $ChromeSource -OutFile $Installer
Then we stop the google chrome process and install the latest version of google chrome using the msiexec command. Then we remove the installer.
Get-Process -Name "Chrome" | Stop-Process -Force
msiexec /i $Installer /qn /norestart
Remove-Item $Installer
That’s it. Very simple very straightforward. If you need to uninstall before installing (My testing was a success with overlapping the installers) then you can do the following before Installing google chrome:
$Chrome = Get-CimInstance win32_Product | where-object {$_.name -like "*Google*Chrome*"}
$Chrome | Invoke-CimMethod -MethodName Uninstall
by David | Jul 30, 2021 | Deployments, Information Technology, PowerShell, Resources
Need to deploy the latest version of firefox to 1000 machines, Here is a little powerhouse script that can do just that. It’s similar to my last script (Gimp) as it downloads directly from the web. This time we don’t have to parse out a website since Modzilla has it laid out before us.
The Script
$FirefoxSource = "https://download.mozilla.org/?product=firefox-latest-ssl&os=win64&lang=en-US"
$Installer = "$ENV:TEMP\ModzillaFirefox.exe"
Invoke-WebRequest -Uri $FirefoxSource -OutFile $Installer
Get-Process -Name "*firefox*" | Stop-Process -Force
Start-Process -FilePath $Installer -ArgumentList "/s" -Verb runas -wait
Remove-Item $Installer
The Breakdown
The source is awesome. We are downloading directly from the site with their latest 64-bit product. This time we are going with the temporary file and then downloading the file with invoke-webrequest. Then we start the process of installing it with the /s flag which means silent. Since we are coming from the temp folder I threw in the runas flag to run it as the system. This way it installs for all users. Next, we set the wait flag to install the system. From everything I have read, we don’t need to uninstall the previous version to install the newest version. We do however have to stop the process that’s why we have a get process and stop process above. Finally, we remove the installer. That’s it. A lot simpler than Gimp.
by David | Jul 30, 2021 | Deployments, Information Technology, PowerShell, Resources
This little script installs the latest version of gimp 2.10 onto your Windows machine. Let’s take a look at the script and then break it down.
The Script
$DownloadPath = "C:\Temp\Gimp"
If (!(Test-Path -Path "C:\Temp\")) {New-Item -Path c:\ -Name Temp -ItemType Directory }
If (!(Test-Path -Path "C:\Temp\Gimp\")) {New-Item -Path c:\Temp -Name Gimp -ItemType Directory }
$URL = "https://download.gimp.org/mirror/pub/gimp/v2.10/windows/"
$Gimp = Invoke-WebRequest -UseBasicParsing -Uri $URL -SessionVariable websession
$Links = $Gimp.Links | Where-Object {$_.href -like "*.exe"} | select-object -Last 1
$URLDownload = "$URL$($Links.href)"
$DownloadName = "$DownloadPath\Gimp.exe"
Invoke-WebRequest -Uri $URLDownload -OutFile $DownloadName
if (Test-path "C:\Program Files\GIMP 2") {
Get-Process -Name "Gimp*" | Stop-Process
Start-Process -FilePath "C:\Program Files\GIMP 2\uninst\unins000.exe" -ArgumentList "/VERYSILENT" -wait
}
Start-Process -FilePath $DownloadName -ArgumentList '/VERYSILENT /NORESTART /ALLUSERS' -wait
Remove-Item $DownloadName
The Breakdown
The first thing we do is set up the path we want to make. Then we test to see if the path exists. If they don’t, we make them. I’m using temp in this cause because I will be deploying this to 2000+ machines. We will remove the installer afterward. I want the Temp folder to existing afterward for future deployments.
$DownloadPath = "C:\Temp\Gimp"
If (!(Test-Path -Path "C:\Temp\")) {New-Item -Path c:\ -Name Temp -ItemType Directory }
If (!(Test-Path -Path "C:\Temp\Gimp\")) {New-Item -Path c:\Temp -Name Gimp -ItemType Directory }
Next, we grab the URL we want to work with This is the gimp’s official download portal. This portal is by default Oldest to newest when you pull from it using Powershell.
$URL = "https://download.gimp.org/mirror/pub/gimp/v2.10/windows/"
Then we use the Invoke-webrequest to grab the website as we did in a previous post. From there we grab all of the links. In this case, since it’s a repo, they are all download links except for 2. We only want the exes of the list, so we use a where-object to find those. Then we select the last 1 as it is the newest version.
$Gimp = Invoke-WebRequest -UseBasicParsing -Uri $URL -SessionVariable websession
$Links = $Gimp.Links | Where-Object {$_.href -like "*.exe"} | select-object -Last 1
Now we need to build our URL and our Path. This is some string controls. Notice the $($Something.Something) in this code. When you deal with an array in a string and want to grab a sub item, you need to call it out with the $().
$URLDownload = "$URL$($Links.href)"
$DownloadName = "$DownloadPath\Gimp.exe"
Next we download the Gimp 2.10 version we are wanting with another invoke-webrequest. This time we select the Outfile tab.
Invoke-WebRequest -Uri $URLDownload -OutFile $DownloadName
Now we want to uninstall the pervious version of Gimp. Since gimp doesn’t show up in the win32_products, we go to it manually in the file system. Newer gimps host themselves inside the program files > gimp 2. So we search to see if that folder exists with a test-path. If it does, we then check to see if gimp is running. Then kill it with fire… ok, not fire, but force. Gimp is awesome about putting an uninstaller inside the file system. So we will use that. It’s located in the Gimp 2 > Uninst > Unins000.exe. Which can be triggered with a /verysilent parameter to keep it quiet. We do this with a start process and we use a flag -wait to wait on it to uninstall.
if (Test-path "C:\Program Files\GIMP 2") {
Get-Process -Name "Gimp*" | Stop-Process -Force
Start-Process -FilePath "C:\Program Files\GIMP 2\uninst\unins000.exe" -ArgumentList "/VERYSILENT" -Wait
}
Then we start the install of the new gimp with the start-process again. We use the Download Name we made eailer with an argument list of /verysilent /norestart /allusers and a -wait.
Start-Process -FilePath $DownloadName -ArgumentList '/VERYSILENT /NORESTART /ALLUSERS' -Wait
Finally we remove the installer with a remote-item.
Remove-Item $DownloadName
That’s all it takes yall. I hope this is helpful to you.
by David | Jul 16, 2021 | Information Technology, PowerShell, Resources
Ever need to combine Get-childitem and Get-ACL while only pulling the access information and users? Well, here we are. I hope you all can use it well.
function Get-SHDACL {
[cmdletbinding()]
param (
[parameter(Mandatory = $true)][string]$Path,
[string]$Filter,
[switch]$Recurse,
[switch]$Directory,
[switch]$File
)
begin {
if ($PSBoundParameters.ContainsKey("filter")) {
if ($File) {
if ($Directory) {
if ($Recurse) {
$SubPath = Get-ChildItem -Path $Path -Recurse -Directory -File -Filter $Filter
} else {
$SubPath = Get-ChildItem -Path $Path -Directory -File -Filter $Filter
}
} else {
if ($Recurse) {
$SubPath = Get-ChildItem -Path $Path -Recurse -File -Filter $Filter
} else {
$SubPath = Get-ChildItem -Path $Path -File -Filter $Filter
}
}
} else {
if ($Directory) {
if ($Recurse) {
$SubPath = Get-ChildItem -Path $Path -Recurse -Directory -filter $Filter
} else {
$SubPath = Get-ChildItem -Path $Path -Directory -Filter $Filter
}
} else {
if ($Recurse) {
$SubPath = Get-ChildItem -Path $Path -Recurse -Filter $Filter
} else {
$SubPath = Get-ChildItem -Path $Path -Filter $Filter
}
}
}
} else {
if ($File) {
if ($Directory) {
if ($Recurse) {
$SubPath = Get-ChildItem -Path $Path -Recurse -Directory -File
} else {
$SubPath = Get-ChildItem -Path $Path -Directory -File
}
} else {
if ($Recurse) {
$SubPath = Get-ChildItem -Path $Path -Recurse -File
} else {
$SubPath = Get-ChildItem -Path $Path -File
}
}
} else {
if ($Directory) {
if ($Recurse) {
$SubPath = Get-ChildItem -Path $Path -Recurse -Directory
} else {
$SubPath = Get-ChildItem -Path $Path -Directory
}
} else {
if ($Recurse) {
$SubPath = Get-ChildItem -Path $Path -Recurse
} else {
$SubPath = Get-ChildItem -Path $Path
}
}
}
}
}
Process {
foreach ($Sub in $SubPath) {
$ACLinfo = Get-Acl -Path $sub.FullName
$Return += foreach ($ACL in $ACLinfo.Access) {
[pscustomobject]@{
Path = $Sub.FullName
FileSystemRights = $ACL.FileSystemRights
ID = $ACL.IdentityReference
Type = $ACL.AccessControlType
Inherited = $ACL.IsInherited
}
}
}
}
end {
$Return
}
}
by David | Jun 21, 2021 | Exchange, Information Technology, PowerShell, Resources
Here is a little powerhouse script I wrote to audit the mailbox sizes. The focus recently is to see who’s mailbox sizes are about to be over and if it’s the deleted folder. I can’t show you all of the scripts, but I can show you part of it. This part will pull the data down in such a way that you can view the mailbox sizes, who has the largest mailboxes, what folder is the largest in those mailboxes, and what their deleted folder is sitting at. Let us take a look at the script itself.
The Script
function Get-SHDEXOMailboxSizeAudit {
[cmdletbinding()]
param (
[pscredential]$Credential
)
Begin {
#Installs required modules
Write-Verbose "Installing required modules"
if (!(Get-InstallEdModule ExchangeOnlineManagement)) { Install-Module ExchangeOnlineManagement }
Write-Verbose "Checking and importing required modules"
# Starts importanting required modules
if (!(Get-Command Connect-ExchangeOnline)) { Import-Module ExchangeOnlineManagement }
}
process {
#Connecting to exchange. Grabing credentials.
if (!($PSBoundParameters.ContainsKey('Credential'))) {
$Credential = (Get-Credential)
}
Connect-ExchangeOnline -credential $Credential
#Grabs all the mailboxes at once. As this report will look at all the mailboxes.
$Mailboxes = Get-Mailbox
#Starts looping through each mailbox
For ($M = 0; $M -le $Mailboxes.count; $M++) {
Write-Verbose "Gathering Info on: $($Mailboxes[$M].UserPrincipalName)"
#Grabs the mailbox stats.
$Stats = Get-EXOMailboxStatistics $mailboxes[$M].UserPrincipalName
$FolderStats = Get-EXOMailboxFolderStatistics $mailboxes[$M].UserPrincipalName
#Starts looping through those folders to get their sizes.
$MainFolderStats = foreach ($Folder in $FolderStats) {
$FolderSize = [math]::Round((($Folder.FolderSize.split('(')[1].split(' ')[0] -replace ',', '') / 1gb), 2)
[pscustomobject]@{
Name = $Folder.name
Size = $FolderSize
}
}
#Adds this information to the mailbox object
$Mailboxes[$M] | add-member -Name "Statistics" -MemberType NoteProperty -Value $Stats
$Mailboxes[$M] | add-member -Name "FolderStats" -MemberType NoteProperty -Value $MainFolderStats
}
#Creates a return value.
$Return = foreach ($Mail in $Mailboxes) {
#Starts looking at mailboxes that are not the discovery mailbox.
if (!($mail.UserPrincipalName -like "DiscoverySearchMailbox*")) {
#Grabs the deleted folder as that is a folder we want to see in this report.
$Deleted = $Mail.FolderStats | where-object { $_.name -like "Deleted Items" }
#Grabs the largest folder. If it's not the deleted folder, then we might want to increase their mailbox sizes.
$LargestFolder = $Mail.FolderStats | Sort-Object Size -Descending | select-object -first 1
#Doing some math on a string. The string format (# bytes). Thus, we work the string to get the bytes. Divide and round.
$Size = [math]::Round(($Mail.Statistics.TotalItemSize.value.tostring().split('(')[1].split(' ')[0].replace(',', '') / 1gb), 2)
#Grabs the mailboxes percentage.
$DeletedToMailboxPercent = [math]::Round((($Deleted.Size / $size) * 100), 0)
#Outputs the data to the return value.
[pscustomobject]@{
DisplayName = $Mail.Displayname
UserPrincipalName = $Mail.UserPrincipalName
MailboxSize = $Size
LargetsFolder = $LargestFolder.Name
LargetsFolderSize = $LargestFolder.Size
DeletedItemSize = $Deleted.Size
DeletedToMailboxPercent = $DeletedToMailboxPercent
}
}
}
#Disconnects exchange
Disconnect-ExchangeOnline -confirm:$false > $null
}
End {
$Return | sort-object MailboxSize -Descending
}
}
The breakdown
First this script is designed to work on powershell 7. It will not work on powershell 5.
The first part we come to is the [pscredential] object. Notice it’s not mandatory. Notice no pipelining either. I have found PS credentials pipped intend to do very poorly. So, it’s simple, bam wam done.
Begin
Inside our begin tab, we have the module setup. We check installed modules for exchangeonlinemanagement. if it’s there, we ignore it and import the module, if it’s not we install it. Same way with importing. If it’s imported, then we do nothing, if it’s not, we import it.
Process
Next, we grab credentials if need be and connect to exchange. We use the get-credential command to grab the credentials. Then we use the connect-exchangeonline command to connect to the exchange online.
Once we are connected, the magic can start. This is the sweetness of this script. The first step is to grab all of the mailboxes at once with Get-Mailbox. Then we start a loop, not any loop, a for a loop. Wait! David, WHY A FOR LOOP! It’s simple, we want to add information to the index. So, we need to be able to call the index. We use the Get-EXOMailboxStatistics and choose the userprincipalname of the index we are looking for. We do the same thing with Get-EXOMailboxFolderStatistics. These gives us some clear stats we can use later on in the script. Now we loop through the folder stats that we just collected and math ourselves some bytes to gb. See the output of the get-exomailboxfolderstatistics looks like “24mb (#### bytes). So we need to filter that out and get the ####. I use a split to do this. I split out the string at the ‘(‘. This way, the bytes are on object 1. Then we split it again by the space. Now the bytes are on the 0 object. Then we replace the ‘,’ with nothing. Now we divide all that by 1gb to convert it to gbs. Then we drop that all into the mainfolderstats. Next, we add all of that information into the mailbox variable we created before this looping madness using add-member. We are adding it as a noteproperty.
Now we have prepped our data, it’s time to sort it. We start the loop once more, but this time it’s simple for each loop as we don’t need the array index value. We first grab all the deleted items folder. Then we grab the largest folder using the sort-object command on the size object of the folderstats note property that we made in the last step. Then we do some math. Like before to get the mailbox overall size. Finally, we grab the deleted mailbox percentage with a little more math. This time its percentage math. Now we have all of this useful information we use our pscustomobject and put it all together.
Then we disconnect using the disconnect-exchangeonline command.
End
Finally we display the return information inside our end tab. We sort the object by the mailbox size in a descending order.