Tuesday, September 2, 2025

Powershell: Microsoft Graph to add new roles to application registration

PowerShell Script
 Bulk add new roles to application registration in azure. Update $roles with ,User
 
# Requires: Microsoft.Graph.Applications module
# Connect to Microsoft Graph with sufficient permissions (e.g., Application.ReadWrite.All)
Connect-MgGraph -scope  "application.readwrite.all" -TenantId "<Tenant-ID>"
# Variables
$AppId = "<App-ID>" # ObjectId of the Azure AD Application

$roles = @"
value,allowedMemberTypes
Group1,User
Group2,User
"@ | ConvertFrom-Csv

$roles = $roles | ForEach-Object {
    [PSCustomObject]@{
        displayName = $_.value
        description = $_.value
        value = $_.value
        allowedMemberTypes = $_.allowedMemberTypes -split ";"
    }
}

# Get the application
$app = Get-MgApplication -ApplicationId $AppId

# Add new roles 
$newRoles = @()
foreach ($role in $roles) {
    $appRole = [Microsoft.Graph.PowerShell.Models.MicrosoftGraphAppRole]::new()
    $appRole.Id = [guid]::NewGuid()
    $appRole.DisplayName = $role.displayName
    $appRole.Description = $role.description
    $appRole.Value = $role.value
    $appRole.AllowedMemberTypes = $role.allowedMemberTypes
    $appRole.IsEnabled = $true
    $appRole.Origin = "Application"
    $newRoles += $appRole
}

# Combine existing roles
$allRoles = @($app.AppRoles) + $newRoles

Update-MgApplication -ApplicationId $AppId -AppRoles $allRoles

Write-Host "Roles added successfully."

Sunday, March 30, 2025

KQL - Group Object Audits ADDS

This is a KQL written for Azure Sentinel. 
Purpose is to search for eventid from Active Directory Domain Services related to Group objects.

SecurityEvent
| where EventID in (4728, 4729, 4732, 4733, 4756, 4757, 4727, 4730, 4731, 4734) // Add or remove from group, create or delete group
| extend 
    Action = case(
        EventID == 4728, "Added to Global Group",
        EventID == 4729, "Removed from Global Group",
        EventID == 4732, "Added to Local Group",
        EventID == 4733, "Removed from Local Group",
        EventID == 4756, "Added to Universal Group",
        EventID == 4757, "Removed from Universal Group",
        EventID == 4727, "Created a Security-Enabled Global Group",
        EventID == 4730, "Deleted a Security-Enabled Global Group",
        EventID == 4731, "Created a Security-Enabled Local Group",
        EventID == 4734, "Deleted a Security-Enabled Local Group",
        "Unknown Action"
    ),
    Initiator = coalesce(tostring(SubjectUserName), tostring(AccountName), "Unknown Initiator")
| summarize 
    FirstOccurrence = min(TimeGenerated)    
    by Action, TargetGroup = TargetAccount, Initiator, Domain = TargetDomainName, MemberName, EventID
| project FirstOccurrence, Action, Initiator, Domain, TargetGroup, MemberName, EventID

Powershell - Search Azure/Entra AD for current status of an employeeID accounts using Graph Batch

Graph is a pain to work with if you are like me and just a scripter 

Takes a list of employee IDs via the $employeeIDs variable 
Queries Azure AD via Microsoft Graph in batches of 20
Retrieves userPrincipalName, employeeId, accountEnabled, 
and LastPasswordChangeDateTime
Outputs results to console and CSV

Connect-MgGraph -Scopes "User.Read.All"

$employeeIds = @"
EMPID
0000001
"@ | ConvertFrom-Csv

$employeeIds = $employeeIds.empid   

# Create batch request body
$batchRequests = @()
$batchSize = 20  # Microsoft Graph allows up to 20 requests per batch
$idCounter = 1

for ($i = 0; $i -lt $employeeIds.Count; $i++) {
    $request = @{
        "id" = "$idCounter"
        "method" = "GET"
        "url" = "/users?`$filter=employeeId eq '$($employeeIds[$i])'&`$select=userPrincipalName,employeeId,accountEnabled,LastPasswordChangeDateTime"
    }
    $batchRequests += $request
    $idCounter++
}

# Split into batches of 20 which i believe is the limit 
$batchedResults = @()
for ($i = 0; $i -lt $batchRequests.Count; $i += $batchSize) {
    $batchEnd = [Math]::Min($i + $batchSize, $batchRequests.Count)
    $currentBatch = $batchRequests[$i..($batchEnd-1)]
    
    $batchBody = @{
        "requests" = $currentBatch
    } | ConvertTo-Json -Depth 10

    # Send batch request
    $response = Invoke-MgGraphRequest -Method POST -Uri "https://graph.microsoft.com/v1.0/`$batch" -Body $batchBody
    
    # Process responses
    foreach ($resp in $response.responses) {
        if ($resp.status -eq 200 -and $resp.body.value) {
            $batchedResults += $resp.body.value | Select-Object @{
                Name = "UserPrincipalName"; Expression = {$_.userPrincipalName}
            }, @{
                Name = "EmployeeID"; Expression = {$_.employeeId}
            }, @{
                Name = "AccountEnabled"; Expression = {$_.accountEnabled}
            }, @{
                Name = "LastPasswordChangeDateTime"; Expression = {$_.LastPasswordChangeDateTime}
            }
        }
    }
}

#Tesults
$batchedResults | Format-Table -AutoSize
$batchedResults | Export-Csv -Path "C:\AzureAD_Employee_Search.csv" -NoTypeInformation

Friday, September 20, 2024

Powershell: Read windows event log remotely and write to csv


#reads event logs for filter and exports to

$Date = (Get-Date).AddMinutes(-30)

$LogName = 'Security'

$ProviderName = "Microsoft-Windows-Security-Auditing"

$EventID  = 6273

$computer = "server.microsoft.com"

Write-Output "Searching $computer"

$Events = Get-WinEvent -ComputerName $computer -FilterHashtable @{
    LogName = $LogName
    ProviderName = $ProviderName
    Id = $EventID
    StartTime = $Date
}

$report = @()

$Events | ForEach-Object -Process {
    [xml]$ConvertedFromXML = $_.ToXml()
    $params = @{} 

    foreach ($entry in $ConvertedFromXML.Event.EventData.Data) {
        $name = $entry.Name
        $Value = $entry.'#text'
        $params[$name] = $Value
    }
    
    $report += [pscustomobject]$params
}

$report | Export-Csv -NoTypeInformation -Path "C:\Temp\Events.csv"

Tuesday, August 15, 2023

Websites that disable right click and highlight bypass

Using Google Chrome or MS Edge:

Disable Javascript post page load.
1. Open console in dev tools
2. press ctrl - alt - p
3. In the run dialog blox type in javascript and select 
"Disable Javascript"

Disable CSS from loading 
1. Open console in dev tools
2. press ctrl - alt - p
3. In the run dialog box type in "Network request blocking"
4. on the drawer that appears check "Enable Network request blocking"
5. Click the plus button and add pattern
6. type in pattern "*.css"

Tuesday, June 20, 2023

Powershell - Microsoft Graph reports - Get MFA status of users.

Requires an App Registration be setup with proper API permissions and a self-signed certificate for authentication to it.


Import-Module
 Microsoft.Graph.Reports

Select-MgProfile -Name "beta"



$TenantId = "XXXXX"

$AppId = "XXXXX"



Connect-MgGraph -ClientId $AppId -TenantId $TenantId -CertificateThumbprint "XXXXXXX"



$report = Get-MgReportCredentialUserRegistrationDetail -all



$report| select @{name="AuthMethods";e={$_.AuthMethods -join ","}},IsCapable,IsEnabled,IsMfaRegistered,IsRegistered,UserDisplayName,UserPrincipalName | Select UserPrincipalName,UserDisplayName,IsCapable,IsEnabled,IsMfaRegistered,IsRegistered,AuthMethods | export-csv -NoTypeInformation -Path "c:\reports\MFA - MFA Audit Logs.csv"



Disconnect-MgGraph

Thursday, June 1, 2023

Custom View for Events NTLMv1 on a DC

 <QueryList>

  <Query Id="0" Path="Security">

    <Select Path="Security">*[System[(EventID=4624)] and EventData[Data[@Name='LmPackageName']='NTLM V1']]</Select>

  </Query>

</QueryList>

Friday, March 17, 2023

Powershell: Get all DNS records from AD DNS

  # Load the DNS Server module
Import-Module DnsServer

# Set the output folder
$outputFolder = "C:\temp\Final"
$DNSServer = DNSServer.com
# Get all DNS zones
$zones = Get-DnsServerZone -ComputerName $DNSServer

# Loop through each zone and export its records to a separate CSV file
foreach ($zone in $zones) {
    $records = Get-DnsServerResourceRecord -ZoneName $zone.ZoneName -ComputerName $DNSServer| `
     select hostname,`
     recordtype,`
     type,timestamp,`
     timetolive,`
     @{n='Data';e={$rr = $_;`
     switch ($rr.RecordType) {
        'A'     {$rr.RecordData.IPv4Address}
        'CNAME' {$rr.RecordData.HostnameAlias}
        'NS' {$rr.RecordData.NameServer}
        'SOA' {$rr.RecordData.PrimaryServer}
        'SRV' {$rr.RecordData.DomainName}
        'PTR' {$rr.RecordData.PtrDomainName}
        'MX' {$rr.RecordData.MailExchange}
        'AAAA' {$rr.RecordData.IPv6Address}
        'TXT' {$rr.RecordData.DescriptiveText}
        }}}
    $outputFile = "$outputFolder\$($zone.ZoneName).csv"
 
    
   $records | Export-Csv -NoTypeInformation -path $outputFile
}

# Output confirmation message
Write-Host "DNS records exported to $outputFolder."

Thursday, February 2, 2023

Powershell: AD DHCP scope information

Needed a script to export dhcp scope and scope id for a domain.


  
  $report = @()
  $DHCPServer = DHCPServerHostname
  $scopes =Get-DhcpServerv4Scope -ComputerName $DHCPServer
  
  foreach ($scope in $Scopes){
    $scopeID = $scope.ScopeId
    $report += Get-DhcpServerv4OptionValue -ScopeId $scopeID -ComputerName $DHCPServer |`
    select @{name="ScopeID";e={$scopeID}},OptionID,Name,Type,@{Name="Value";e={$_.value -join ";"}
  }
    
    $report | export-csv -NoTypeInformation -path "c:\temp\DHCPScopeIDwithOption.csv"
    $scopes | export-csv -NoTypeInformation -path "c:\temp\DHCPScopeIDwithMask.csv"

Monday, August 9, 2021

Powershell - Extract ADFS certificates from ADFS server

Wrote script so people specify the adfs server and the signature and encryption certificate will extract the certs to two files stored in c:\temp


# just put in the ADFS server name 

$AdfsServer = 'adfs.microsoft.com'



[xml]$XmlDocument = (New-Object System.Net.WebClient).DownloadString("https://$AdfsServer/FederationMetadata/2007-06/FederationMetadata.xml")

#ADFS Signing Certificate

$Cert = $xmldocument.entitydescriptor.roledescriptor.keydescriptor | select Use,@{Name="x509"; Expression={(($_.keyinfo).X509data).x509certificate}}

#Sign Cert

$cert | ? {$_.use -eq "signing"} | select -ExpandProperty x509 | out-file c:\temp\Signcert.cer

#Encryption Cert

$cert | ? {$_.use -eq "encryption"} | select -ExpandProperty x509 | out-file c:\temp\Encryptioncert.cer



$Cert

Wednesday, July 29, 2020

Powershell - Get certificate information

 List Certificate Templates



function get-CertificateTemplates {
[CmdletBinding()] Param (
     [Parameter(Mandatory = $True, ValueFromPipelineByPropertyName = $True)]
     [string]$forest
    )
$DefaultPartition = Get-ADDomainController -Server $forest | select -expand DefaultPartition


$configcontext = "CN=Configuration,$($DefaultPartition)"
$ADSI = [ADSI]"LDAP://CN=Certificate Templates,CN=Public Key Services,CN=Services,$ConfigContext"

$ADSI.Children | Sort-Object Name | Select-Object DisplayName, Name, msPKI-Cert-Template-OID
}


List Certificate CDP info

function get-CertificateCDP {
[CmdletBinding()] Param (
     [Parameter(Mandatory = $True, ValueFromPipelineByPropertyName = $True)]
     [string]$forest
    )

$DefaultPartition = Get-ADDomainController -Server $forest | select -expand DefaultPartition


$configcontext = "CN=Configuration,$($DefaultPartition)"
$ADSI = [ADSI]"LDAP://CN=cdp,CN=Public Key Services,CN=Services,$ConfigContext"

$ADSI.Children  | select cn,Children,path
}

List Certificate Auhtorities in forest - requires activedirectory module

function get-CertificationAuthorities {
[CmdletBinding()] Param (
     [Parameter(Mandatory = $True, ValueFromPipelineByPropertyName = $True)]
     [string]$forest
    )
  

$DefaultPartition = Get-ADDomainController -Server $forest | select -expand DefaultPartition


$configcontext = "CN=Configuration,$($DefaultPartition)"
$ADSI = [ADSI]"LDAP://CN=Certification Authorities,CN=Public Key Services,CN=Services,$ConfigContext"

$ADSI.Children | select name,whenCreated

} 

Monday, June 29, 2020

Powershell : get-ADReplicationReport - Function to get replication status from specified domain controller


.Synopsis
   Get Active Directory replication report
DESCRIPTION
   reports on replication issues
EXAMPLE
   get-ADReplicationReport -domains "Domain1","Domain2"

function get-QrgADReplicationReport
{
    [CmdletBinding()]
    [Alias()]
    [OutputType([int])]
    Param
    (
        [Parameter(Mandatory=$true,
                   ValueFromPipelineByPropertyName=$true,
                   Position=0)]
        [string]$Server
        
    )

    Begin
    {
    
    }
    Process
    {
    
        Get-ADReplicationPartnerMetadata -Target $server -Partition * | select `
        Server,`
        @{Name = 'Partner';Expression = {$_.Partner.split(",")[1].split("=")[1]}},`
        Partition,`
        LastReplicationSuccess,`
        ConsecutiveReplicationFailures,`
        PartnerType,`
        PartnerAddress,`
        PartnerGuid
    
    }
    
    End
    {
       
    }
}

Wednesday, June 5, 2019

Powershell : Function to get FSMO Info for a domain

function Get-DomainFSMO 
{
[CmdletBinding()]
 [OutputType([System.Management.ManagementObject])]
 param
    (
     [parameter( Mandatory=$false,
                    ValueFromPipeline=$True,
                    Position = 0,
                    HelpMessage="An array of domains.", 
                 ValueFromPipelineByPropertyName=$True)]
     [String[]] $domain
    )
    begin{Write-Verbose "Getting Forest info for: $CurrentDomain"}
    process{
        foreach ($CurrentDomain in $domain){

          $dom = @{
                                label="Domain"
                                expression = {$CurrentDomain}
                            }
         
                
         Write-Verbose "Getting Forest info for: $CurrentDomain"
         Get-ADDomain -Server $CurrentDomain | Select-Object $dom,InfrastructureMaster, RIDMaster, PDCEmulator,DomainMode,DomainSid
         
  
    }
    }
    end{}
 
}

Friday, July 27, 2018

vmware - Powershell get hardware inventory

Need a script to get serial numbers from my esxi hosts and a few other items
PowerState        : PoweredOn
Version           : 6.0.0
NumCpu            : 12
Cluster           : Clustername
MaxEVCMode        : intel-haswell
vCenter           : vcentername
Name              : esxi host name
Build             : buildnumber
CpuCoreCountTotal : 12
Model             : Hardware model number
MemoryTotalGB     : Total memory of esxi host
CpuModel          : cpu model
MemoryUsageGB     : memory usage at time of report
SerialNumber      : esxi host serial number
ConnectionState   : Connected
Prior to running this make sure you are up to date on your vmware powercli via
Install-Module -Name VMware.PowerCLI
Import-Module VMware.VimAutomation.Core
Set-PowerCLIConfiguration -Scope User -ParticipateInCEIP $false
Set-PowerCLIConfiguration -InvalidCertificateAction ignore -confirm:$false
Update-Module -Name VMware.PowerCLI
$vcenter = "vcenterhostname"

#Tony Unger
#Requires -Version 5

<#If script fails try to run the following commands 

Install-Module -Name VMware.PowerCLI
Import-Module VMware.VimAutomation.Core
Set-PowerCLIConfiguration -Scope User -ParticipateInCEIP $false
Set-PowerCLIConfiguration -InvalidCertificateAction ignore -confirm:$false
Update-Module -Name VMware.PowerCLI

#>

& 'C:\Program Files (x86)\VMware\Infrastructure\PowerCLI\Scripts\Initialize-PowerCLIEnvironment.ps1'
$report =@()

Connect-VIServer -Server $vcenter


$vmHosts = get-vmhost | select Name,`
ConnectionState,`
PowerState,`
NumCpu,`
MemoryUsageGB,`
MemoryTotalGB,`
Version,`
Build,`
MaxEVCMode,`
@{N="Cluster";E={ $_.Parent}},`
@{N="vCenter";E={ ($_.uid).split("@")[1].split(":")[0]}} 

foreach ($vmHost in $vmHosts) {

$VMHardwareInfo = get-vmhosthardware -vmhost $vmHost.name |select Manufacturer,`
Model,`
SerialNumber,`
CpuModel,`
CpuCoreCountTotal

  $ESXiInfo = New-Object -TypeName PSObject -Property @{ 
                        Name = $vmHost.name 
                        ConnectionState = $vmHost.ConnectionState
                        PowerState = $vmHost.PowerState 
                        NumCpu = $vmHost.NumCpu 
                        MemoryUsageGB = $vmHost.MemoryUsageGB             
                        MemoryTotalGB = $vmHost.MemoryTotalGB 
                        Version = $vmHost.Version 
                        Build = $vmHost.Build
                        MaxEVCMode = $vmHost.MaxEVCMode 
                        Cluster = $vmHost.Cluster
                        vCenter = $vmHost.vCenter 
                        Model = $VMHardwareInfo.Model 
                        SerialNumber = $VMHardwareInfo.SerialNumber             
                        CpuModel = $VMHardwareInfo.CpuModel 
                        CpuCoreCountTotal = $VMHardwareInfo.CpuCoreCountTotal 
                       
                    }      
        $report += $ESXiInfo



}


  $report | export-csv -path c:\temp\ESXiHardwareInfo.csv -notype

Wednesday, April 4, 2018

Azure: powershell one liner to set one user account password to never expire.

Install-Module -Name MSOnline
Connect-MsolService
$username = "username"
(get-msoluser -UserPrincipalName  $username | select -expand objectID).guid | %{Set-MsolUser -ObjectId $_ -PasswordNeverExpires $true}

Wednesday, September 20, 2017

Powershell : Certutil Find Expired Certs on CA server

Wrote this to get certificate expiration information for certificates that expired 5 days ago to ones that expire in 90 days. Wrap an invoke-command around this for remote query.
$Before = (get-date).adddays(90).ToString("MM/dd/yyyy")
$After = (get-date).AddDays(-5).ToString("MM/dd/yyyy")
<#

https://blogs.technet.microsoft.com/poshchap/2016/01/01/powershell-and-certutil-exe/
We create a date range with
$Before, i.e. certificates expiring before this date, and
$After, i.e. certificates expiring after this date. These values are converted into something that certutil can understand - $Restrict. This is then used with the certutil -restrict parameter.
#>
$Restrict = "NotAfter<=$Before,NotAfter>=$After"
$Report = @()
$cmd = & certutil.exe -view -restrict $Restrict -out "RequesterName,CommonName,Certificate Expiration Date","Certificate Template"

$SplitLines = $cmd.Split("`n`r")

$Index = 0
foreach ($line in $SplitLines){

    if ($line -like "Row*" ){
        $Details = New-Object PSObject 
        $Details | Add-Member noteProperty "RequesterName" $SplitLines[$index+1].split(":")[1].Replace("`"","").Replace(" ","")
        $Details | Add-Member noteProperty "CommonName" $SplitLines[$index+2].split(":")[1].Replace("`"","").Replace(" ","")
        $Details | Add-Member noteProperty "Certificate Expiration Date" $SplitLines[$index+3].split(':')[1].split(" ")[1].Replace(" ","")

        
        if ($SplitLines[$index+4].split(":")[1].Replace("`"","") -notlike "*1.*") {
            $TemplateName = $SplitLines[$index+4].split(":")[1].Replace("`"","").Replace(" ","")
        }
        Else {
        write-host "hit"
        $templatename = $SplitLines[$index+4].split(":")[1].Replace("`"","").split(" ")[2].Replace(" ","")
        }

        $Details | Add-Member noteProperty "Certificate Template" $TemplateName
        
        
        
        $report += $Details 
    
    }

    $Index++
}
$report

Tuesday, September 12, 2017

Powershell: Generate CSR

Wrote this function to generate SAN certificate requests. This isn't a robust solution, nor does it follow best practice, it is more of a hey it works with a Microsoft CA.


 #https://social.technet.microsoft.com/Forums/Lync/en-US/b4e27454-c60f-4346-9f7d-22214f49ab6f/create-inf-file-to-create-req-using-certreqexe?forum=winserversecurity

Found a more correct script after i wrote this here https://pscsr256.codeplex.com/

<#
.Synopsis
   Generate CSR by Tony Unger
.DESCRIPTION
   Generates CSR
.EXAMPLE
   new-csr -CommonName "test0.microsoft.com" -DNSNames $HostNames
.EXAMPLE
   new-csr -CommonName "test0.microsoft.com"
#>
function new-csr
{
    [CmdletBinding()]
    [Alias()]
    [OutputType([int])]
    Param
    (
        # Common name for request
        [Parameter(Mandatory=$true,
                   ValueFromPipelineByPropertyName=$true,
                   Position=0)]
        $CommonName,

        # SAN DNS names
        [string[]]
        $DNSNames
    )

    Begin
    {
    $Date = (Get-Date).ToString('ddMMyyyy')
    $ReqFile = "Cert_Req-$CommonName-" + "$Date" + ".csr"
    }
    Process
    {
        $InfFile = @"
        [NewRequest]`r
        Subject = "CN=$CommonName"`r
        KeySpec = 1
        KeyLength = 2048
        Exportable = TRUE`r
        RequestType = CMC`r
        [Extensions] 
        2.5.29.17 = "{text}" 
        _continue_ = "dns=$CommonName&"`n
"@
    foreach ($DNSName in $DNSNames){
        $InfFile =$InfFile + @" 
            _continue_ = "dns=$DNSName&"`n
"@


    }
    $InfFile
        $FinalInfFile = "Cert_Req_Inf-$CommonName-" + "$Date" + ".inf"
        New-Item $FinalInfFile -type file -value $InfFile -Force

        cmd /c "certreq -new $FinalInfFile $ReqFile"
    }
    End
    {
    }
}

$Hostnames = "test1.microsoft.com","test2.microsoft.com"
new-csr -CommonName "test0.microsoft.com" -DNSNames $HostNames

Thursday, July 6, 2017

Set non-domain windows server to use a KMS Server

REM List out KMS Servers
nslookup -type=all _vlmcs._tcp

REM Set workgroup to use a particular KMS Server
slmgr.vbs /skms <KMS server>:<port>

Tuesday, May 9, 2017

Powershell: Get LUN ID with Diskspace

get-wmiobject Win32_DiskDrive | select name,caption, scsibus, scsilogicalunit, @{Name="size(GB)";Expression={"{0:N1}" -f($_.size/1gb)}} | sort-object name

Tuesday, October 18, 2016

Powershell: Change UPN on list of users

I needed a script to bulk change a list of users to a new UPN i came up with this.

#
$Users = gc c:\temp\users.txt | get-ADUser

foreach ($User in $Users)
$UserUPN = $User.UserPrincipalname
$UserUPNwithOutDomain = ([regex]::matches($UserUPN, "([^@]+)")).value[0]
Set-ADUser $User -userprincipalname "$UserUPNwithOutDomain@microsoft.com"
}

Powershell: Microsoft Graph to add new roles to application registration

PowerShell Script Bulk add new roles to application registration in azure. Update $roles with ,Us...