Translate

Showing posts with label Powershell. Show all posts
Showing posts with label Powershell. Show all posts

Wednesday, January 21, 2026

Powershell - how to gather data with your scripts in Azure Log Analytics

For specific use cases (e.g. to collect the state of your secure boot certificate updates) it might be required to run a script on your hosts and collect the data centrally. The best way to achieve this is to use Azure Log Analytics. 

Here is how you can achieve this:


1. Setup the Azure Log Analytics Workspace in Azure 

1.1 Search in the Azure Portal for "Azure Log Analytics Workspace" and add a new one.

Hint: Formerly Microsoft was showing this data in the portal but they removed it. Its still supported but not the preferred method anymore. Any other methods does not allow the PowerShell based upload. Instead you need the AMA client to gather data. Which does not work for a PowerShell script based solutions were you want to gather your own custom generated data.


2. Gather the required data (WorkspaceID and Preshared Key) for your script

2.1 connect with Powershell to your Subscription

2.2 Ensure Azure modules are loaded (Import-Module Az) or you use the Azure Cloud shell (easiest method)

2.3 Retrieve the Workspace ID (replace RessourceGroup name and Workspace name!)

(Get-AzOperationalInsightsWorkspace -ResourceGroupName rg-xxx -Name "law-xxx-test").CustomerID



Shown ID is your Workspace ID (note/copy it we need it later again!)


2.4 Retrieve the Workspace Keys for uploading

Get-AzOperationalInsightsWorkspaceSharedKeys -ResourceGroupName rg-xxx -Name "law-xxx-test"


Shown SharedKeys are required for the script as well. Please note / copy them and keep the data in a save place!

3. Powershell Code to send some data
For demonstration purposes we gather hostnamen and IP addresses. So you can simply modify the relevant part. This thing will generate your first data.

If there is no log created with this name (here MyHostLog) then it will be generated automatically for you when you send the first data.

Keep in mind you need to modify the former captured WorkspaceID and Workspace Key (I would recommend you use the primary key).

function Send-Ip {
 # Function to log Host Name and IP Address to custom log in Azure Log Analytics!
 # Simply use start-transcript at the beginning your script and 
 # stop-transcript as last line in your script for easy logging. 
 # This will catch up all the write-host and other output as well!
    Write-Host "Log Host Name and IP Address to custom log"
    $apiVersion = "2016-04-01"
 # IMPORTANT! Adjust the code with your own data gathered in step 2!
    $workspaceId = "11111111-2222-3333-4444-555555555555"
    $workspaceKey = "wJxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx0ww=="
   
 # Name of your log in your Azure Log Analytics Workspace
    $logType = "MyHostLog"
 # Generation of some sample custom log data. Change this for your purpose
    $hostname = $env:COMPUTERNAME
    $ipAddress = (Get-NetIPAddress -AddressFamily IPv4 | Where-Object { $_.InterfaceAlias -match "Ethernet*" }).IPAddress
    $timestamp = Get-Date -Format "yyyy-MM-ddTHH:mm:ssZ"
 # Creation of the log entry. Its good to have a timestamp and may be a host reference if you want! And add here whatever you want!
    $logEntry = @(
        @{
            TimeGenerated = $timestamp
            Hostname      = $hostname
            IPAddress     = $ipAddress
        }
    )
 # Generate log entry body and header to send. It will be encrypted and signed with your shared Workspace key from above.
    $body = $logEntry | ConvertTo-Json -Depth 3
    # Ensure the date is in RFC1123 format
    $date = [DateTime]::UtcNow.ToString("r")
    # Construct the string to sign
    $stringToHash = "POST`n$($body.Length)`napplication/json`nx-ms-date:$date`n/api/logs"
    $hmacsha256 = New-Object System.Security.Cryptography.HMACSHA256
    $hmacsha256.Key = [Convert]::FromBase64String($workspaceKey)
    $hash = $hmacsha256.ComputeHash([Text.Encoding]::UTF8.GetBytes($stringToHash))
    $signature = [Convert]::ToBase64String($hash)
    $authkey = $workspaceId + ":" + $signature
    $authorization = "SharedKey $authkey"
    $headers = @{
        "Content-Type"  = "application/json"
        "Authorization" = $authorization
        "Log-Type"      = $logType
        "x-ms-date"     = $date
    }
# Lets look into the header we have created (this is just for reference in your local logs if you want!)
   Write-Host "Header:"
   $headers
   Write-Host "=================================================="
# Lets look into the body we have created (this is just for reference in your local logs if you want!)
    Write-Host "Body:"
    $body
# Now lets send the data to the workspace (may be you want to adjust the catch block & finally text)
    try {
                $response = Invoke-RestMethod -Method Post -Uri "https://$workspaceId.ods.opinsights.azure.com/api/logs?api-version=$apiVersion" -Headers $headers -Body $body
                # Output the response (so it got catched in start-transcript log, no response is fine as well!)
                Write-Host "Response: $response"         
        }
        catch {
            Write-Error "Failed to send the IpReport: $_"
        }
        finally {
            Write-Host "Processing done for IP reporting!"
        }
}
# Now call the function
Send-Ip


Thats it!  Simply ajdust the sample code as you want. 

You find in Azure Log Analytics workspace table "MyHostLog_CL" your first new entry:



Enjoy the show!

Friday, January 17, 2020

Powershell - replace files in use

UPDATED - 02/08/2020

If you want to replace a file in use you need to handle a very strange registry key called "PendingFileRenameOperations" with a REG_MULTI_SZ type.

If you handle this manually you will absolutely run in trouble. As this key is rarely empty. So you would have to read, append and write again. The chance that you corrupt this thing is very high.

Better to use the builtin function for this. The API is called: MoveFileEx 
More Infos about this API you will find here:https://docs.microsoft.com/en-gb/windows/win32/api/winbase/nf-winbase-movefileexa



The file will be replaced during a reboot. This is especially handy when files are locked during regular OS operation. You can even delete a locked file (check API documentation for this).

As this is an unmanaged code API (The real Win32 world without .Net extensions) you need a little bit more code to make it work like a .Net API class. So pure [.NETAPI CALL]::APINameAndOptions does not work alone!

To get a clue how to use it in PowerShell you find here an example:

# Function definition for the API MoveFileEx in PowerShell

Add-Type -TypeDefinition @'
using System;
using System.Runtime.InteropServices; 

    [Flags]
    public enum MoveFileFlags
    {
        MOVEFILE_REPLACE_EXISTING = 0x00000001,
        MOVEFILE_COPY_ALLOWED = 0x00000002,
        MOVEFILE_DELAY_UNTIL_REBOOT = 0x00000004,
        MOVEFILE_WRITE_THROUGH = 0x00000008,
        MOVEFILE_CREATE_HARDLINK = 0x00000010,
        MOVEFILE_FAIL_IF_NOT_TRACKABLE = 0x00000020
    }

public static class Kernel32
    {
        [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
        public static extern bool MoveFileEx(string lpExistingFileName, string lpNewFileName,MoveFileFlags dwFlags);
    }
'@

# Calling the function in Powershell with an example
# [kernel32]::MoveFileEx(OLDFILE,NEWFILE,"MOVEFILE_DELAY_UNTIL_REBOOT")
# Parameter Options: 
#   1. Option 1 is new fullpath filename and should have a temporary name.
#   2. Option 2 is for file in use (when API-NULL is given for option 2 then file from option 1 will be deleted)
#      IMPORTANT:  Use [NullString]::Value     the Powershell version of $Null does not get passed to the API properly!
#   3. Parameter which will tell the replacement (or even delete) of locked file during reboot!
#   4. For proper replacement you need 2 calls. 1st call with API specific NULL value will delete the original file. The 2nd call will rename the new file with temp name to the original file
#   5. To retrieve the API extended error properties in case of a failure the command is extended by $Lasterror with capturing the component model exception

# 1st Call to delete the old original file first! 
[kernel32]::MoveFileEx("C:\temp\Test1.txt",[NullString]::Value,"MOVEFILE_DELAY_UNTIL_REBOOT");$LastError = [ComponentModel.Win32Exception][Runtime.InteropServices.Marshal]::GetLastWin32Error()

# 2nd Call to rename the new temporary file with the old originial file name. Which now has new content!
[kernel32]::MoveFileEx("C:\temp\Test2.txt","C:\temp\Test1.txt", "MOVEFILE_DELAY_UNTIL_REBOOT");$LastError = [ComponentModel.Win32Exception][Runtime.InteropServices.Marshal]::GetLastWin32Error()


TROUBLESHOOTING:
1. Use Sysinternals "PendMoves.exe" to check the status and valid entries of registry key for "PendingFileRenameOperations"
2. Registrykey "PendingFileRenameOperations" can be checked also manually at: HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager
 a. When the key does not exist - no further rename operations exist
 b. When an operation fails during the reboot the key will be deleted also. So nothing is kept!
 c. DO NEVER EVER edit this key manually! Its a binary stored Multi_REG_SZ. Whatever you do it will just be worse!
3. For more log information's check also C:\Windows\PFRO.log




0xc0000035 indicates that you missed the delete the original file first!
0xc0000034 indicates that the file is already deleted. So you can ignore it!

Thursday, November 22, 2018

Network connection from public to private with Powershell

Sometimes the Windows 10 network connection will be classified automatically as public or private and it is not in the way as it should be. As this will affect firewall rules you sometimes get by a domain group policy. The effect is that your software cant communicate anymore when this was done wrong.

How to fix this?

Very simple with PowerShell!

1. Open an Admin PowerShell Command Prompt.
2. Type in: Get-NetConnectionProfile
3. Check the name from your network connection. Keep in mind when you have Security features like credential guard and/or HyperV enabled you will see more "Unidentified networks". You can safely ignore them. Here in my example the network is called "CAP".
4. Type in: Set-NetConnectionProfile -Name "CAP" -NetworkCategory Private

Your setting will be active immediately!





Wednesday, November 21, 2018

Azure AD - DSRegCMD output checked in Powershell

Sometimes you have to deal with DSREGCMD Output.

Means the interesting output of DSREGCMD need to be further analyzed in PowerShell.

Here a useful example I found. 

Keep in mind the array (@) is just containing 4 examples.
May be you need to extend it for further. 





$template = @'
        AzureAdJoined : {AzureAdJoined*:YES}
     EnterpriseJoined : {EnterpriseJoined:NO}
        AzureAdJoined : {AzureAdJoined*:NO}
     EnterpriseJoined : {EnterpriseJoined:YES}
        AzureAdJoined : {AzureAdJoined*:NO}
     EnterpriseJoined : {EnterpriseJoined:NO}

        AzureAdJoined : {AzureAdJoined*:YES}
     EnterpriseJoined : {EnterpriseJoined:YES}
'@


PS C:\> dsregcmd /status | ConvertFrom-String -TemplateContent $template


AzureAdJoined EnterpriseJoined
------------- ----------------
NO            NO

Friday, March 2, 2018

SCCM automated driver management

Its always a pain to handle driver updates with each new release of Windows 10. The most simplest way to fix this is simply use Microsoft Surface products. Drivers and Bios Updates are deployed simply through Windows Update and all major headaches are gone. :-)

But unfortunately the world is not that simply. And as always there is a solution too.

Simply use the Driver Automation Tool




Thanks to Maurice Daly (MVP)
https://gallery.technet.microsoft.com/scriptcenter/Driver-Tool-Automate-9ddcc010

The Driver Automation Tool is a GUI developed in PowerShell which provides full automation of BIOS and driver downloads, extraction, packaging and distribution with Dell, HP, Lenovo & Microsoft client hardware.
The intuitive GUI provides you with a full list of models from the supported manufacturer, allowing you to select one or many models, it also will detect Dell and Lenovo models matched against the WMI models known to ConfigMgr.


How Does It Work?
When the tool is opened, you have the option to select your manufacturer and OS of choice. When you click on the Find Models button, the tool initiates a download of XML content from the selected manufacturer, reads in the XML and displays a full list of models for selection. Clicking on the Add to Import List adds each of these models for processing and once you click on the Start Download and Import Process button the tool starts the full process to automatically download and package the content.


MDT Support
Although primarily designed for use with ConfigMgr, the tool also supports MDT. Here you will find the ability to select your deployment shares as well as dynamic creation of folder hierarchies based on total control naming methods


FEATURES: ConfigMgr
  • Site server selection
  • Automatic site code discovery
  • Distribution point and distribution point group selection
  • Binary differential replication
  • Distribution priority
  • Clean up of unused drivers
  • Removal of superseded driver packages
  • Removal of source download packages
  • Driver & BIOS piloting
  • Driver & BIOS deployment state management (production, pilot, retired)
FEATURES: MDT
  • Auto or manual selection of the MDT PS module
  • Deployment share listing
  • Folder structure naming
FEATURES: Settings
All settings are saved into an XML file open close, and read back into the tool upon launch.


Thursday, January 11, 2018

WMI Explorer 2.0 - including Powershell Code generation

While working with PowerShell you come sometimes to situations you need to control something on the machine not reachable right out of the box. So WMI is the answer to your question. Lots of things you can only trigger by WMI through PowerShell when it comes to automation also in combination with SCCM.

The main pain point is how to find the right thing at the right place. And then how to bring it into a PowerShell query. For both questions an easy answer is WMI Explorer.


 

As there is a specific script tab to create the needed PowerShell code.
Here as example to figure out the used Processor.


Requirements
Features
  •  Browse and view WMI objects in a single pane of view.
  •  Connect as alternate credentials to remote computers.
  •  Asynchronous and Synchronous mode for enumeration.
  • Method execution.
  • SMS (Configuration Manager) mode providing additional functionality for Configuration Manager.
  • Filter classes and instances matching specified criteria.
  • View classes/instances in Managed Object Format (MOF).
  • Search classes, methods and properties for names matching specified criteria.
  • Run WQL queries.
  • Automatic generation of WQL query for the selected Class/Instance.
  • Automatic script creation (PowerShell and VBS).
  • Highlighting enumerated objects.
  • Display property descriptions and possible enumeration values (if available).
  • Display methods descriptions and parameters.
  • Display embedded property values.
  • Caching enumerated classes/instances.
  • View WMI Provider Process Information.
  • Automatic check for new version.
  • Added option to specify COMPUTERNAME as a parameter and automatically connect.
    Example: WmiExplorer.exe COMPUTERNAME
 DOWNLOAD IT HERE