Add exceptions for file paths from azure defender to adaptive application security controls - azure

I have a bunch of machines being monitored by adaptive application security controls that are giving warnings because the training process was not ran long enough to recognize benign executables. What's an easy way to add exceptions for the executables in active alerts to the adaptive security groups?

There's already an existing recommendation that might provide what you are trying to do:
https://learn.microsoft.com/en-us/azure/defender-for-cloud/adaptive-application-controls#respond-to-the-allowlist-rules-in-your-adaptive-application-control-policy-should-be-updated-recommendation

This script grabs the active alerts from defender, and updates the groups.
The alerts must still be dismissed manually.
function Get-ExistingRules {
Param(
$subscriptionId,
$groupName
)
$url = "https://management.azure.com/subscriptions/$subscriptionId/providers/Microsoft.Security/locations/centralus/applicationWhitelistings/${groupName}?api-version=2015-06-01-preview";
return az rest `
--method get `
--url $url `
| ConvertFrom-Json;
}
function Add-NewRules {
Param(
$subscriptionId,
$groupName,
$files
)
$url = "https://management.azure.com/subscriptions/$subscriptionId/providers/Microsoft.Security/locations/centralus/applicationWhitelistings/${groupName}?api-version=2015-06-01-preview";
$existing = Get-ExistingRules $subscriptionId $groupName;
$existing | ConvertTo-Json -Depth 100 > asd.json;
$myList = $existing.properties.pathRecommendations;
foreach ($file in $files) {
$myList += [pscustomobject]#{
path = $file.path
type = "File"
common = $true
action = "Add"
usernames = #(
[pscustomobject]#{
username = "Everyone"
recommendationAction = "Recommended"
}
)
userSids = #(
"S-1-1-0"
)
fileType = $file.type
configurationStatus = "NotConfigured"
};
}
$existing.properties.pathRecommendations = $myList;
$existing.properties = [pscustomobject]#{
protectionMode = $existing.properties.protectionMode
vmRecommendations = $existing.properties.vmRecommendations
pathRecommendations = $existing.properties.pathRecommendations
}
$existing.PSObject.properties.remove("location");
# return $existing;
$body = $existing | ConvertTo-Json -Depth 20 -Compress;
$body > temp.json;
# $body = $body -replace "`"", "\`"";
# return az rest `
# --method PUT `
# --url $url `
# --body $body `
# | ConvertFrom-Json;
# avoid max command length limit by storing body in a file
try {
return az rest `
--method PUT `
--url $url `
--body `#temp.json `
| ConvertFrom-Json;
}
catch {
Write-Warning "Encountered error adding rule";
Write-Warning "$_";
}
return $null;
}
function Format-Body {
param(
$obj
)
$body = $obj | ConvertTo-Json -Depth 100 -Compress;
$body = $body -replace "`"", "\`"";
$body = $body -replace "`r", "";
return $body;
}
Write-Host "Listing subscriptions";
# you can filter to just one subscription if you want
# $subscriptions = az account list --query "[?name=='NPRD'].id" --output tsv;
$subscriptions = az account list --query "[].id" --output tsv;
$allAlerts = New-Object System.Collections.ArrayList;
$i = 0;
foreach ($sub in $subscriptions) {
Write-Progress -Id 0 -Activity "Fetching alerts" -Status $sub -PercentComplete ($i / $subscriptions.Count * 100);
$i = $i++;
$alerts = az security alert list `
--subscription $sub `
| ConvertFrom-Json `
| Where-Object { #("VM_AdaptiveApplicationControlLinuxViolationAudited", "VM_AdaptiveApplicationControlWindowsViolationAudited") -contains $_.alertType } `
| Where-Object { $_.status -eq "Active" };
foreach ($x in $alerts) {
$allAlerts.Add($x) > $null;
}
}
Write-Progress -Id 0 "Done" -Completed;
function Get-Files {
Param(
$alert
)
if ($alert.alertType -eq "VM_AdaptiveApplicationControlLinuxViolationAudited") {
$fileType = "executable";
}
else {
$fileType = "exe";
}
$pattern = "Path: (.*?);";
$str = $alert.extendedProperties.file;
return $str `
| Select-String -Pattern $pattern -AllMatches `
| ForEach-Object { $_.Matches } `
| ForEach-Object { $_.Value } `
| ForEach-Object { [pscustomobject]#{
path = $_
type = $fileType
}};
}
$alertGroups = $allAlerts | Select-Object *, #{Name = "groupName"; Expression = { $_.extendedProperties.groupName } } | Group-Object groupName;
foreach ($group in $alertGroups) {
$groupName = $group.Name;
$group.Group[0].id -match "/subscriptions/([^/]+)/" > $null;
$subscriptionId = $matches[1];
$files = $group.Group | ForEach-Object { Get-Files $_ };
Write-Host "Adding file path rule sub=$subscriptionId group=$groupName count=$($files.Count)";
Add-NewRules $subscriptionId $groupName $files;
}

Related

How to pull all tags at resource level in azure

I am Trying to pull All the Tags resource level in azure
$subscripionList = Get-AzSubscription
foreach ($subscriptionId in $subscripionList) {
Write-Host 'Getting details about SubscriptionId :' $subscriptionId
Set-AzContext -Subscription $subscriptionId
#Select-AzureRmSubscription -SubscriptionName $subscriptionId
$resourcesgroups = Get-AzResourceGroup
foreach($resourcesgroup in $resourcesgroups){
Write-Host 'resourcegroup :' $resourcesgroup
$resources = Get-AzResource -ResourceGroupName $resourcesgroup.ResourceGroupName
#$azure_resources = Get-AzResource
foreach($resource in $resources){
Write-Host $resource
{
#Fetching Tags
$Tags = $resource.Tags
#Checkign if tags is null or have value
if($Tags -ne $null)
{
foreach($Tag in $Tags)
{
$TagsAsString += $Tag.Name + ":" + $Tag.Value + ";"
}
}
else
{
$TagsAsString = "NULL"
}
}
}
}
I am Trying to get all the subscription then
I am Trying to get all the resource groups then
I am Trying to get all the resource present in resource group
Trying to get all the tags
But i am unable to get Tags and can anyone guide me how to export all tags into csv file.
As there is no $tag.Name and $Tag.Value , Your code doesn't store any values. The output you are looking are stored as $tags.keys and $tags.values. So, to collect them you will have to use for loop again . I tested the same by replacing the for each loop for the resources with the below script:
$TagsAsString=#()
$tagkeys =#()
$TagValues =#()
$resources = Get-AzResource -ResourceGroupName <resourcegroupname>
foreach($resource in $resources){
Write-host ("ResourceName :")$resource.Name
if($resource.Tags -ne $null){
foreach($Tag in $resource.Tags){
foreach($key in $Tag.keys){
$tagkeys += $key
}
foreach($value in $Tag.values){
$TagValues += $value
}
}
}
else{
Write-Host ("No Tags")
}
}
for($i = 0; $i -lt $tagkeys.Length; $i++) {
$TagsAsString=( '{0} : {1}' -f $tagkeys[$i], $tagvalues[$i] )
Write-Host $TagsAsString
}
Output:

deploy Lab with windows image with azure Labservices

I try to create a lab Windows 10 or Windows Server in a lab account with PowerShell but he doesn't found any image except when I put :
Image = 'Centos-Based ' he creates a lab with centos-based 8.1
my code please :
# Create a Lab with Windows server
$la = Get-AzLabAccount -ResourceGroupName $ResourceGroupName -LabAccountName $LabAccountName
Write-Host "$LabAccountName lab account created or found."
#param (
$LabName = Read-Host ' Name of Your lab '
$Image = Read-Host ' Name of Your lab '
$Size = Read-Host ' Size of Your lab '
$InstallGpuDriverEnabled = $false
$UserName = Read-Host ' UserName of Your lab '
$Password = Read-Host ' Password of Your lab '
$UsageQuotaInHours = 10
$SharedPasswordEnabled = $false
$idleGracePeriod = 15
$idleOsGracePeriod = 0
$idleNoConnectGracePeriod = 15
$TemplateVmState = "Enabled"
#)
$img = $la | Get-AzLabAccountGalleryImage | Where-Object {$_.name -like $Image} | Select-Object -First 1
if(-not $img -or $img.Count -ne 1) {Write-Error "$Image pattern doesn't match just one image."}
Write-Host "Image $Image found."
begin { }
process {
try {
foreach ($la in $LabAccount) {
$labAccountUri = (ConvertToUri -resource $la)
$createUri = $labAccountUri + "/createLab"
$labUri = $labAccountUri + "/labs/" + $LabName
$environmentSettingUri = $labUri + "/environmentsettings/default"
$sharedPassword = if ($SharedPasswordEnabled) { "Enabled" } else { "Disabled" }
$imageType = if ($Image.id -match '/galleryimages/') { 'galleryImageResourceId' } else { 'sharedImageResourceId' }
InvokeRest -Uri $createUri -Method 'Post' -Body (#{
name = $LabName
labParameters = #{
$imageType = $Image.id
password = $Password
username = $UserName
userQuota = "PT$($UsageQuotaInHours.ToString())H"
vmSize = $Size
sharedPasswordState = $sharedPassword
templateVmState = $TemplateVmState
idleShutdownMode = $idleShutdownMode
idleGracePeriod = "PT$($idleGracePeriod.ToString())M"
enableDisconnectOnIdle = $enableDisconnectOnIdle
idleOsGracePeriod = "PT$($idleOsGracePeriod.ToString())M"
enableNoConnectShutdown = $enableNoConnectShutdown
idleNoConnectGracePeriod = "PT$($idleNoConnectGracePeriod.ToString())M"
installGpuDriverEnabled = $gpuDriverState
}
} | ConvertTo-Json) | Out-Null
}
$lab = WaitProvisioning -uri $labUri -delaySec 60 -retryCount 120
WaitProvisioning -uri $environmentSettingUri -delaySec 60 -retryCount 120 | Out-Null
return $lab
}
catch {
Write-Error -ErrorRecord $_ -EA $callerEA
}
}
end { }
$lab = $la | New-AzLab -LabName $LabName -Image $img -Size $size -UserName $userName -Password $password -UsageQuotaInHours $usageQuota | Publish-AzLab
Write-Host "$LabName lab doesn't exist. Created it."
Regarding the issue, please update the expression as Where-Object {$_.name -like "Windows 10*"}.
For exmaple
$la = Get-AzLabAccount -ResourceGroupName $ResourceGroupName -LabAccountName $LabAccountName
$img = $la | Get-AzLabAccountGalleryImage | Where-Object {$_.name -like "Windows 10*"} | Select-Object -First 1

Issue Creating SSL/TLS secure channel

The below given PowerShell Script Basically Checks for Backup Jobs inside Backup Vault and Sends a daily backup job report to emails mentioned. I am Facing issues as the script throws an error
Invoke-RestMethod : The request was aborted: Could not create SSL/TLS secure channel
I am using SendGrid Api Key in a Kv used for the mailing service. Have tried adding an updated TLS Version too it still throws the same error.
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
Remove-Variable -Name * -ErrorAction SilentlyContinue
$numberofdays = 2
$DAILYBACKUPSTATS = #()
$DAILYBACKUPSTATS1 = #()
$DAILYBACKUPSTATSFAILED = #()
$DAILYBACKUPSTATSFAILED1 = #()
$Subscriptions = Get-AzSubscription
$SubscriptionIDs = $Subscriptions.ID
foreach ($SubscriptionID in $SubscriptionIDs) {
$Subscription = Set-AzContext -SubscriptionId $SubscriptionID
$backupvaults = Get-AzRecoveryServicesVault
$backupvaultnames = $backupvaults.name
foreach ($backupvaultname in $backupvaultnames)
{
$backupvault = Get-AzRecoveryServicesVault -Name $backupvaultname
$startdate = [system.Datetime]::UtcNow
$enddate = ([system.Datetime]::UtcNow).AddDays(1)
for ( $i = 1; $i -le $numberofdays; $i++ ) {
# We query one day at a time
$dailyjoblist = Get-AzRecoveryServicesBackupJob -VaultId $backupvault.ID -From $startdate -To $enddate -Operation Backup;
foreach ( $job in $dailyjoblist ) {
#Extract the information for the reports
$message.Body
$newstatsobj = New-Object System.Object
$newstatsobj | Add-Member -type NoteProperty -name Subscription -value (Get-AzSubscription -SubscriptionId $SubscriptionID).Name
$newstatsobj | Add-Member -type NoteProperty -name Date -value $job.StartTime
$newstatsobj | Add-Member -type NoteProperty -name RecoveryVaultName -value $backupvaultname
$newstatsobj | Add-Member -type NoteProperty -name VMName -value $job.WorkloadName
$newstatsobj | Add-Member -type NoteProperty -name Duration -value $job.Duration
$newstatsobj | Add-Member -type NoteProperty -name Status -value $job.Status
$details = Get-AzRecoveryServicesBackupJob -VaultId $backupvault.ID -Job $job
$DAILYBACKUPSTATS += $newstatsobj
}
$enddate = $enddate.AddDays(-1)
$startdate = $startdate.AddDays(-1)
#Sets the columns
$a = "<style>"
$a = $a + "BODY{background-color:white;}"
$a = $a + "TABLE{border-width: 3px;border-style: double;border-collapse: collapse;font-family: Calibri;}"
$a = $a + "TH{border-width: 3px;padding: 2px;border-style: double;background-color:SandyBrown;font-family: Calibri;}"
$a = $a + "TD{border-width: 3px;padding: 2px;border-style: double;font-family: Calibri;}"
$a = $a + "</style>"
}
}
}
$DAILYBACKUPSTATS += $DAILYBACKUPSTATS1
$EmailBody = "<table width='70%'><tbody>"
$EmailBody += "<tr>"
$EmailBody += "<td width='100%' colSpan=4><font face='Calibri' color='#003399' size='3'>Azure Recovery Services Vault Backup Job Status<BR>NOTE: Date is UTC<BR><BR>"
$EmailBody += "</tr>"
$EmailBody += "</table>"
$message1 = $DAILYBACKUPSTATS | ConvertTo-Html -Head $a
$EmailBody += $message1
$content = $emailBody
$VaultName = "Parent-kv"
$SENDGRID_API_KEY = (Get-AzKeyVaultSecret -VaultName $VaultName -Name "Sendgrd-api").SecretValue
$headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]"
$headers.Add("Authorization", "Bearer " + $SENDGRID_API_KEY)
$headers.Add("Content-Type", "application/json")
$destEmailAddress = "jon#mail.com"
$fromEmailAddress = "Azure-NoReply#cla.com"
$body = #{
personalizations = #(
#{
to = #(
#{
email = $destEmailAddress
}
)
}
)
from = #{
email = $fromEmailAddress
}
subject = $subject
content = #(
#{
type = "text/html"
value = $content
}
)
}
$bodyJson = $body | ConvertTo-Json -Depth 4
$response = Invoke-RestMethod -Uri https://api.sendgrid.com/v3/mail/send -Method Post -Headers $headers -Body $bodyJson
You can most likely search here for a whole bunch of potential remedies.
The request was aborted: Could not create SSL/TLS secure channel
I would also recommend to:
verify that the credentials ($SENDGRID_API_KEY) is correct.
run a network sniffer to see the SSL handshake and see if there any exceptions.

Azure DB sync logs in log analytics workspace

I have an Azure SQL database sync group that is scheduled to run each hour.
Question:
Can i sent this logs to a log analytics workspace by enabling the diagnostic settings?
If yes, what will be the best way to filter them out?
I can successfully get the logs from powershell, but my end goal here is to create an alert based on the sync logs.
Thanks you in advance!
If you want to send Azure SQL database sync group to a log analytics workspace, you can implement it with HTTP Data Collector API.
For example
$SubscriptionId = "SubscriptionId"
$DS_ResourceGroupName = ""
$DS_ServerName = ""
$DS_DatabaseName = ""
$DS_SyncGroupName = ""
# Replace with your OMS Workspace ID
$CustomerId = "OMSCustomerID"
# Replace with your OMS Primary Key
$SharedKey = "SharedKey"
# Specify the name of the record type that you'll be creating
$LogType = "DataSyncLog"
# Specify a field with the created time for the records
$TimeStampField = "DateValue"
Connect-AzureRmAccount
select-azurermsubscription -SubscriptionId $SubscriptionId
#get log
$endtime =[System.DateTime]::UtcNow
$StartTime = ""
$Logs = Get-AzureRmSqlSyncGroupLog -ResourceGroupName $DS_ResourceGroupName `
-ServerName $DS_ServerName `
-DatabaseName $DS_DatabaseName `
-SyncGroupName $DS_SyncGroupName `
-starttime $StartTime `
-endtime $EndTime;
if ($Logs.Length -gt 0)
{
foreach ($Log in $Logs)
{
$Log | Add-Member -Name "SubscriptionId" -Value $SubscriptionId -MemberType NoteProperty
$Log | Add-Member -Name "ResourceGroupName" -Value $DS_ResourceGroupName -MemberType NoteProperty
$Log | Add-Member -Name "ServerName" -Value $DS_ServerName -MemberType NoteProperty
$Log | Add-Member -Name "HubDatabaseName" -Value $DS_DatabaseName -MemberType NoteProperty
$Log | Add-Member -Name "SyncGroupName" -Value $DS_SyncGroupName -MemberType NoteProperty
#Filter out Successes to Reduce Data Volume to OMS
#Include the 5 commented out line below to enable the filter
#For($i=0; $i -lt $Log.Length; $i++ ) {
# if($Log[$i].LogLevel -eq "Success") {
# $Log[$i] =""
# }
# }
}
$json = ConvertTo-JSON $logs
$result = Post-OMSData -customerId $customerId -sharedKey $sharedKey -body ([System.Text.Encoding]::UTF8.GetBytes($json)) -logType $logType
if ($result -eq 200)
{
Write-Host "Success"
}
if ($result -ne 200)
{
throw
#"
Posting to OMS Failed
Runbook Name: DataSyncOMSIntegration
"#
}
Function Build-Signature ($customerId, $sharedKey, $date, $contentLength, $method, $contentType, $resource)
{
$xHeaders = "x-ms-date:" + $date
$stringToHash = $method + "`n" + $contentLength + "`n" + $contentType + "`n" + $xHeaders + "`n" + $resource
$bytesToHash = [Text.Encoding]::UTF8.GetBytes($stringToHash)
$keyBytes = [Convert]::FromBase64String($sharedKey)
$sha256 = New-Object System.Security.Cryptography.HMACSHA256
$sha256.Key = $keyBytes
$calculatedHash = $sha256.ComputeHash($bytesToHash)
$encodedHash = [Convert]::ToBase64String($calculatedHash)
$authorization = 'SharedKey {0}:{1}' -f $customerId,$encodedHash
return $authorization
}
# Create the function to create and post the request
Function Post-OMSData($customerId, $sharedKey, $body, $logType)
{
$method = "POST"
$contentType = "application/json"
$resource = "/api/logs"
$rfc1123date = [DateTime]::UtcNow.ToString("r")
$contentLength = $body.Length
$signature = Build-Signature `
-customerId $customerId `
-sharedKey $sharedKey `
-date $rfc1123date `
-contentLength $contentLength `
-method $method `
-contentType $contentType `
-resource $resource
$uri = "https://" + $customerId + ".ods.opinsights.azure.com" + $resource + "?api-version=2016-04-01"
$headers = #{
"Authorization" = $signature;
"Log-Type" = $logType;
"x-ms-date" = $rfc1123date;
"time-generated-field" = $TimeStampField;
}
$response = Invoke-WebRequest -Uri $uri -Method $method -ContentType $contentType -Headers $headers -Body $body -UseBasicParsing
return $response.StatusCode
}
After doing that, you can alert vai log search in Azure log analysis. For more detail, please refer to the blog.

Display all sites and bindings in PowerShell

I am documenting all the sites and binding related to the site from the IIS. Is there an easy way to get this list through a PowerShell script rather than manually typing looking at IIS?
I want the output to be something like this:
Site Bindings
TestSite www.hello.com
www.test.com
JonDoeSite www.johndoe.site
Try this:
Import-Module Webadministration
Get-ChildItem -Path IIS:\Sites
It should return something that looks like this:
Name ID State Physical Path Bindings
---- -- ----- ------------- --------
ChristophersWeb 22 Started C:\temp http *:8080:ChristophersWebsite.ChDom.com
From here you can refine results, but be careful. A pipe to the select statement will not give you what you need. Based on your requirements I would build a custom object or hashtable.
Try something like this to get the format you wanted:
Get-WebBinding | % {
$name = $_.ItemXPath -replace '(?:.*?)name=''([^'']*)(?:.*)', '$1'
New-Object psobject -Property #{
Name = $name
Binding = $_.bindinginformation.Split(":")[-1]
}
} | Group-Object -Property Name |
Format-Table Name, #{n="Bindings";e={$_.Group.Binding -join "`n"}} -Wrap
If you just want to list all the sites (ie. to find a binding)
Change the working directory to "C:\Windows\system32\inetsrv"
cd c:\Windows\system32\inetsrv
Next run "appcmd list sites" (plural) and output to a file. e.g c:\IISSiteBindings.txt
appcmd list sites > c:\IISSiteBindings.txt
Now open with notepad from your command prompt.
notepad c:\IISSiteBindings.txt
The most easy way as I saw:
Foreach ($Site in get-website) { Foreach ($Bind in $Site.bindings.collection) {[pscustomobject]#{name=$Site.name;Protocol=$Bind.Protocol;Bindings=$Bind.BindingInformation}}}
Try this
function DisplayLocalSites
{
try{
Set-ExecutionPolicy unrestricted
$list = #()
foreach ($webapp in get-childitem IIS:\Sites\)
{
$name = "IIS:\Sites\" + $webapp.name
$item = #{}
$item.WebAppName = $webapp.name
foreach($Bind in $webapp.Bindings.collection)
{
$item.SiteUrl = $Bind.Protocol +'://'+ $Bind.BindingInformation.Split(":")[-1]
}
$obj = New-Object PSObject -Property $item
$list += $obj
}
$list | Format-Table -a -Property "WebAppName","SiteUrl"
$list | Out-File -filepath C:\websites.txt
Set-ExecutionPolicy restricted
}
catch
{
$ExceptionMessage = "Error in Line: " + $_.Exception.Line + ". " + $_.Exception.GetType().FullName + ": " + $_.Exception.Message + " Stacktrace: " + $_.Exception.StackTrace
$ExceptionMessage
}
}
function Get-ADDWebBindings {
param([string]$Name="*",[switch]$http,[switch]$https)
try {
if (-not (Get-Module WebAdministration)) { Import-Module WebAdministration }
Get-WebBinding | ForEach-Object { $_.ItemXPath -replace '(?:.*?)name=''([^'']*)(?:.*)', '$1' } | Sort | Get-Unique | Where-Object {$_ -like $Name} | ForEach-Object {
$n=$_
Get-WebBinding | Where-Object { ($_.ItemXPath -replace '(?:.*?)name=''([^'']*)(?:.*)', '$1') -like $n } | ForEach-Object {
if ($http -or $https) {
if ( ($http -and ($_.protocol -like "http")) -or ($https -and ($_.protocol -like "https")) ) {
New-Object psobject -Property #{Name = $n;Protocol=$_.protocol;Binding = $_.bindinginformation}
}
} else {
New-Object psobject -Property #{Name = $n;Protocol=$_.protocol;Binding = $_.bindinginformation}
}
}
}
}
catch {
$false
}
}
I found this page because I needed to migrate a site with many many bindings to a new server. I used some of the code here to generate the powershell script below to add the bindings to the new server. Sharing in case it is useful to someone else:
Import-Module WebAdministration
$Websites = Get-ChildItem IIS:\Sites
$site = $Websites | Where-object { $_.Name -eq 'site-name-in-iis-here' }
$Binding = $Site.bindings
[string]$BindingInfo = $Binding.Collection
[string[]]$Bindings = $BindingInfo.Split(" ")
$i = 0
$header = ""
Do{
[string[]]$Bindings2 = $Bindings[($i+1)].Split(":")
Write-Output ("New-WebBinding -Name `"site-name-in-iis-here`" -IPAddress " + $Bindings2[0] + " -Port " + $Bindings2[1] + " -HostHeader `"" + $Bindings2[2] + "`"")
$i=$i+2
} while ($i -lt ($bindings.count))
It generates records that look like this:
New-WebBinding -Name "site-name-in-iis-here" -IPAddress "*" -Port 80 -HostHeader www.aaa.com
I found this question because I wanted to generate a web page with links to all the websites running on my IIS instance. I used Alexander Shapkin's answer to come up with the following to generate a bunch of links.
$hostname = "localhost"
Foreach ($Site in get-website) {
Foreach ($Bind in $Site.bindings.collection) {
$data = [PSCustomObject]#{
name=$Site.name;
Protocol=$Bind.Protocol;
Bindings=$Bind.BindingInformation
}
$data.Bindings = $data.Bindings -replace '(:$)', ''
$html = "" + $data.name + ""
$html.Replace("*", $hostname);
}
}
Then I paste the results into this hastily written HTML:
<html>
<style>
a { display: block; }
</style>
{paste PowerShell results here}
</body>
</html>

Resources