Menu

Showing posts with label DevOps Automation. Show all posts
Showing posts with label DevOps Automation. Show all posts

11 Jul 2026

πŸ›‘️ Automating SSL Certificate Access and Distribution using Forms and Power Automate.

⏱ Estimated reading time: 5 minutes

  • Automate SSL certificate requests and sharing within your organization.
  • Uses Microsoft Forms, Power Automate, SharePoint Online, and Exchange Online.
  • Includes email validation, approval, access assignment, and secure delivery.
  • Designed for Microsoft 365, Middleware, DevOps, and Enterprise Support teams.


Introduction

Manual SSL certificate sharing often requires repeated emails, approvals, permission changes, and file transfers. This becomes difficult to manage when many applications and users are involved.

Using Microsoft Forms and Power Automate, the process can be standardized from request submission to approval, SharePoint access, and certificate delivery.

Key Concept:
Automation reduces manual effort while approval and access controls keep certificate sharing secure and auditable.

Business Challenge

The manual process introduced several operational and security challenges:

  • Requester details were verified manually.
  • Requests did not follow a standard format.
  • Approvals and delivery required repeated follow-ups.
  • Sensitive files could be shared through uncontrolled channels.
  • Tracking request, approval, and delivery history was difficult.

Solution Overview

Microsoft Forms collects the request, Power Automate manages the workflow, SharePoint Online stores the certificate, and Outlook sends notifications.

Component Purpose
Microsoft Forms Captures certificate request details.
Power Automate Manages validation, approval, access, and delivery.
Approval Connector Sends the request to the Certificate Administrator.
SharePoint Online Stores certificate files and manages reader access.
Exchange Online / Outlook Sends certificate, private key, and rejection emails.

Power Automate Workflow Diagram

The flow validates the requester before approval. Approved requests receive the certificate and SharePoint access, while rejected or unauthorized requests are stopped.

F Microsoft Forms
When a New Response is Submitted Microsoft Forms Trigger
F Microsoft Forms
Get Response Details Retrieve Name, Email ID, Application Name
Condition 1
Validate Organization Email Domain
True
A Approvals
Start and Wait for an Approval Approval request sent to Certificate Administrator
Condition 2
Check Approval Outcome
True
O Office 365 Outlook
Send an Email (V2) 1 Certificate file and SharePoint path access
O Office 365 Outlook
Send an Email (V2) 2 Private key file
False
O Office 365 Outlook
Send an Email (V2) Request rejection notification
False
Control
Terminate Invalid or unauthorized email domain
Workflow Logic:
A valid corporate email proceeds to approval. Approved requests receive the certificate and private key through separate emails. Rejected or invalid requests do not receive access.

Step 1: Certificate Request Form

Users submit requests through Microsoft Forms. The form captures:

  • Requester Name
  • Email Address
  • Application Name

Submitting the form automatically starts the Power Automate flow.


Step 2: Organizational Email Validation

Power Automate verifies that the requester belongs to an approved corporate domain.

endsWith(emailAddress,'@company.com')
Security Control:
Requests from non-approved email domains are terminated automatically.

Step 3: Approval Workflow

After validation, the Certificate Administrator receives an approval request containing:

  • Requester Name
  • Email Address
  • Application Name
  • Request Timestamp

The administrator selects Approve or Reject. No certificate or private key is sent until the request is approved.


Step 4: Automated Certificate Distribution

After approval, Power Automate sends two separate emails.

Email 1 – Certificate Delivery

Subject: Certificate Request Approved

Attachments:
certificate.crt
certificate.pem

Body:
Your certificate request has been approved.
SharePoint read access has been granted.

Email 2 – Private Key Delivery

Subject: Private Key File

Attachment:
privatekey.key

Body:
Please find the private key file attached.
This file is confidential and should be stored securely.
Security Best Practice:
Use encryption, password-protected files, restricted access, or secure vault-based storage for stronger protection.

Step 5: SharePoint Access Assignment

After approval, Power Automate grants Reader access to the relevant SharePoint library or folder and sends the repository link.

User Approved
      │
      ▼
Power Automate
      │
      ▼
Grant SharePoint Reader Access
      │
      ▼
Send Repository Link

This removes manual permission assignment and ensures access is granted only after approval.


Security Design

Control Purpose
Email Domain Validation Blocks external or unauthorized requests.
Approval Workflow Ensures certificate sharing is authorized.
Separate Email Delivery Reduces exposure of the certificate and private key together.
SharePoint Reader Access Provides controlled repository access.
Power Automate Run History Tracks each workflow execution.
Approval History Records approval and rejection decisions.
Enterprise Recommendation:
Store certificate files and private keys in separate secured repositories and retain request, approval, and access logs.

Key Takeaways

  • Microsoft Forms provides a standard request portal.
  • Power Automate manages validation, approval, and delivery.
  • SharePoint provides controlled certificate access.
  • Certificate and private key files are delivered separately.
  • The solution improves security, efficiency, and auditability.

Real-World Outcome:
A manual email-based activity becomes a secure, automated, and auditable SSL certificate sharing process.

Category: Microsoft 365 Automation, Power Automate, SharePoint Online, Enterprise Security, SSL Certificate
Author: Pradeep V
Blog: MiddlewareBox.com


3 Apr 2026

πŸ” WAS Service Restart Using WinRM over HTTPS (5986) on Azure DevOps

WAS Service Restart Using WinRM over HTTPS (5986) on Azure DevOps

This section explains the prerequisites, end‑to‑end pipeline flow, and a real‑world production use case for restarting Windows services securely using Azure DevOps and WinRM over HTTPS (5986).


πŸ“š Table of Contents


πŸ“Œ Prerequisites

πŸ”· Azure DevOps

  • Azure DevOps project and pipeline created
  • Manual trigger enabled for UAT / PROD
  • Pipeline parameters (START / STOP / RESTART)
  • Secrets stored in Variable Groups

πŸͺŸ Windows DevOps Agent

  • Self‑hosted Windows agent installed
  • Agent pool access configured
  • Outbound connectivity to target servers
  • PowerShellOnTargetMachines available

πŸ–₯️ Target Windows Server

  • WinRM enabled and running , Refrence link ( Secure WinRM Configuration Guide )
  • WinRM configured over HTTPS (5986)
  • CA SSL certificate installed
  • 5986 allowed in Firewall / NSG
  • Service credentials available

⬆️ Back to Top


πŸ”„ Pipeline Flow: Service Restart Using WinRM over Secure Port (5986)

┌──────────────────────────────────────────────┐
│           Azure DevOps Pipeline              │
│          (Manual Trigger)                    │
└──────────────────────────────────────────────┘
                    │
                    ▼
┌──────────────────────────────────────────────┐
│      Parameter Selection (OPERATION)         │
│  ─ START_WAS                                 │
│  ─ STOP_WAS                                  │
│  ─ RESTART_WAS                               │
└──────────────────────────────────────────────┘
                    │
                    ▼
┌──────────────────────────────────────────────┐
│   Azure DevOps Windows Agent                 │
│   Pool: AppOps-Win-AgentPool                 │
│   Secrets: WinRM-Secrets (secure)            │
└──────────────────────────────────────────────┘
                    │
                    ▼
┌──────────────────────────────────────────────┐
│      Secure WinRM Session Initiated          │
│  ─ Protocol   : HTTPS                        │
│  ─ Port       : 5986                         │
│  ─ Auth       : Negotiate                    │
│  ─ SSL Cert   : CA Enterprise Certificate    │
└──────────────────────────────────────────────┘
                    │
                    ▼
┌──────────────────────────────────────────────┐
│        Target Windows Server                 │
│        WebSphere Application Server          │
└──────────────────────────────────────────────┘
                    │
                    ▼
┌──────────────────────────────────────────────┐
│   PowerShellOnTargetMachines@                │
│   Remote Session via WinRM                   │
└──────────────────────────────────────────────┘
                    │
                    ▼
┌──────────────────────────────────────────────┐
│        Operation Execution Logic             │
└──────────────────────────────────────────────┘
        │                 │                 │
        ▼                 ▼                 ▼
┌───────────────┐ ┌────────────────┐ ┌──────────────────────┐
│  START_WAS    │ │   STOP_WAS     │ │    RESTART_WAS       │
│ Start Service │ │ Stop Service   │ │ Stop → Cleanup       │
│ Validate Run  │ │ Validate Stop  │ │ Start → Validate     │
└───────────────┘ └────────────────┘ └──────────────────────┘
                    │
                    ▼
┌──────────────────────────────────────────────┐
│   Logs Printed to Azure DevOps Console       │
│  ─ Pre/Post Service Status                   │
│  ─ Cleanup Verification                      │
│  ─ Immediate Failure on Error                │
└──────────────────────────────────────────────┘
                    │
                    ▼
┌──────────────────────────────────────────────┐
│            Pipeline Result                   │
│   SUCCESS → Safe Completion                  │
│   FAILURE → Clear Error Output               │
└──────────────────────────────────────────────┘

⬆️ Back to Top


WAS Service Restart Using WinRM over HTTPS (5986) on Azure DevOps

πŸ“Œ Code Example Pipeline (Azure DevOps YAML)

Below is a working Azure DevOps YAML pipeline example to START / STOP / RESTART an IBM WebSphere (WAS) Windows service using WinRM over HTTPS (5986). Secrets are stored securely in a Variable Group.


# ============================================================
# PIPELINE: WinRM (HTTPS 5986) – START / STOP / RESTART WAS Service
# TARGET  : 172.23.XX.XX
# SERVICE : IBMWAS85Service - AppNode1
# AGENT   : AppOps-Win-AgentPool
# SECRETS : Variable Group "WinRM-Secrets" (expects remotePassword)
# ============================================================

# Manual run only (no CI trigger)
trigger: none

# Azure DevOps agent pool (Windows agent recommended for this task)
pool:
  name: AppOps-Win-AgentPool

# Runtime choice: which action to perform
parameters:
  - name: OPERATION
    displayName: "Select RESTART-APPNODE1 operation"
    type: string
    default: RESTART_WAS
    values:
      - START_WAS
      - STOP_WAS
      - RESTART_WAS

# Variables + Secret Variable Group
variables:
  # Secret variable group should contain:
  # - remotePassword (secret)
  - group: WinRM-Secrets

  # Target server (WinRM endpoint)
  - name: remoteHost
    value: "172.23.XX.XX"

  # WinRM HTTPS port
  - name: httpsPort
    value: "5986"

  # WAS Windows Service name (must match service name on target)
  - name: wasServiceName
    value: "IBMWAS85Service - AppNode1"

  # PSSession options (relaxed cert validation for internal IP use cases)
  # NOTE: For PROD hardening, remove Skip* flags and use proper cert CN/SAN.
  - name: pssOptions
    value: "-SkipCNCheck -SkipCACheck -SkipRevocationCheck -IdleTimeout 7200000 -OperationTimeout 0 -OutputBufferingMode Block"

steps:
  # ============================================================
  # START WAS
  # ============================================================
  # Runs ONLY when OPERATION = START_WAS
  - ${{ if eq(parameters.OPERATION, 'START_WAS') }}:
      - task: PowerShellOnTargetMachines@3
        displayName: "WAS START (Windows Service)"
        inputs:
          # Remote host + WinRM port
          Machines: "$(remoteHost):$(httpsPort)"

          # Local admin user on target (change to domain user if needed)
          UserName: ".\\username"

          # Password from secret variable group (WinRM-Secrets)
          UserPassword: "$(remotePassword)"

          # WinRM protocol
          CommunicationProtocol: "Https"

          # Default authentication (Negotiate)
          AuthenticationMechanism: "Default"

          # Session options passed to New-PSSessionOption on agent side
          NewPsSessionOptionArguments: "$(pssOptions)"

          # Fail fast on any error
          ErrorActionPreference: "Stop"
          FailOnScriptError: true

          # Single target execution
          RunPowershellInParallel: false

          # Inline PowerShell executed on target
          ScriptType: "Inline"
          InlineScript: |
            # Stop immediately if any command fails
            $ErrorActionPreference = 'Stop'

            # Service name from pipeline variable
            $service = '$(wasServiceName)'

            Write-Host "============================================================"
            Write-Host "ACTION  : START_WAS"
            Write-Host "SERVICE : $service"
            Write-Host "HOST    : $env:COMPUTERNAME"
            Write-Host "USER    : $(whoami)"
            Write-Host "============================================================"

            Write-Host "Starting service: $service"
            Start-Service -Name $service
            Start-Sleep -Seconds 5

            $status = (Get-Service -Name $service).Status
            Write-Host "Current service status: $status"

            if ($status -ne 'Running') {
              throw "WAS service failed to start"
            }

            Write-Host "RESULT: WAS service is RUNNING"

  # ============================================================
  # STOP WAS
  # ============================================================
  # Runs ONLY when OPERATION = STOP_WAS
  - ${{ if eq(parameters.OPERATION, 'STOP_WAS') }}:
      - task: PowerShellOnTargetMachines@3
        displayName: "WAS STOP (Service + Cleanup)"
        inputs:
          Machines: "$(remoteHost):$(httpsPort)"
          UserName: ".\\username"
          UserPassword: "$(remotePassword)"
          CommunicationProtocol: "Https"
          AuthenticationMechanism: "Default"
          NewPsSessionOptionArguments: "$(pssOptions)"
          ErrorActionPreference: "Stop"
          RunPowershellInParallel: false
          FailOnScriptError: true
          ScriptType: "Inline"
          InlineScript: |
            $ErrorActionPreference = 'Stop'

            $service     = '$(wasServiceName)'
            $profilePath = 'D:\IBM\WebSphere\AppServer\profiles\AppSrv02'
            $wstempPath  = Join-Path $profilePath 'wstemp'
            $tempPath    = Join-Path $profilePath 'temp'

            Write-Host "============================================================"
            Write-Host "ACTION  : STOP_WAS"
            Write-Host "SERVICE : $service"
            Write-Host "PROFILE : $profilePath"
            Write-Host "HOST    : $env:COMPUTERNAME"
            Write-Host "============================================================"

            Write-Host "Stopping service: $service"
            Stop-Service -Name $service -Force
            Start-Sleep -Seconds 5

            $status = (Get-Service -Name $service).Status
            Write-Host "Current service status: $status"

            if ($status -ne 'Stopped') {
              throw "WAS service failed to stop"
            }

            # Timestamped rename = safe backup (NO deletion)
            $timestamp = Get-Date -Format "yyyyMMdd_HHmmss"

            if (Test-Path $wstempPath) {
              Write-Host "Renaming wstemp -> wstemp_$timestamp"
              Rename-Item $wstempPath "wstemp_$timestamp" -Force
            } else {
              Write-Host "INFO: wstemp not found: $wstempPath"
            }

            if (Test-Path $tempPath) {
              Write-Host "Renaming temp   -> temp_$timestamp"
              Rename-Item $tempPath "temp_$timestamp" -Force
            } else {
              Write-Host "INFO: temp not found: $tempPath"
            }

            # Cleanup scripts (standard WAS maintenance)
            Write-Host "Running: clearClassCache.bat"
            & "$profilePath\bin\clearClassCache.bat"

            Write-Host "Running: osgiCfgInit.bat"
            & "$profilePath\bin\osgiCfgInit.bat"

            Write-Host "RESULT: STOP completed successfully (service stopped + cleanup done)"

  # ============================================================
  # RESTART WAS
  # ============================================================
  # Runs ONLY when OPERATION = RESTART_WAS
  - ${{ if eq(parameters.OPERATION, 'RESTART_WAS') }}:
      - task: PowerShellOnTargetMachines@3
        displayName: "WAS RESTART (Stop + Cleanup + Start)"
        inputs:
          Machines: "$(remoteHost):$(httpsPort)"
          UserName: ".\\username"
          UserPassword: "$(remotePassword)"
          CommunicationProtocol: "Https"
          AuthenticationMechanism: "Default"
          NewPsSessionOptionArguments: "$(pssOptions)"
          ErrorActionPreference: "Stop"
          RunPowershellInParallel: false
          FailOnScriptError: true
          ScriptType: "Inline"
          InlineScript: |
            $ErrorActionPreference = 'Stop'

            $service     = '$(wasServiceName)'
            $profilePath = 'D:\IBM\WebSphere\AppServer\profiles\AppSrv02'
            $wstempPath  = Join-Path $profilePath 'wstemp'
            $tempPath    = Join-Path $profilePath 'temp'

            Write-Host "============================================================"
            Write-Host "ACTION  : RESTART_WAS"
            Write-Host "SERVICE : $service"
            Write-Host "PROFILE : $profilePath"
            Write-Host "HOST    : $env:COMPUTERNAME"
            Write-Host "============================================================"

            # -------- 1) STOP --------
            Write-Host "Stopping service: $service"
            Stop-Service -Name $service -Force
            Start-Sleep -Seconds 5

            $status = (Get-Service -Name $service).Status
            Write-Host "Status after STOP: $status"

            if ($status -ne 'Stopped') {
              throw "WAS service failed to stop"
            }

            # -------- 2) CLEANUP (NO delete, only rename) --------
            $timestamp = Get-Date -Format "yyyyMMdd_HHmmss"

            if (Test-Path $wstempPath) {
              Write-Host "Renaming wstemp -> wstemp_$timestamp"
              Rename-Item $wstempPath "wstemp_$timestamp" -Force
            } else {
              Write-Host "INFO: wstemp not found: $wstempPath"
            }

            if (Test-Path $tempPath) {
              Write-Host "Renaming temp   -> temp_$timestamp"
              Rename-Item $tempPath "temp_$timestamp" -Force
            } else {
              Write-Host "INFO: temp not found: $tempPath"
            }

            Write-Host "Running: clearClassCache.bat"
            & "$profilePath\bin\clearClassCache.bat"

            Write-Host "Running: osgiCfgInit.bat"
            & "$profilePath\bin\osgiCfgInit.bat"

            # -------- 3) START --------
            Write-Host "Starting service: $service"
            Start-Service -Name $service
            Start-Sleep -Seconds 8

            $status = (Get-Service -Name $service).Status
            Write-Host "Status after START: $status"

            if ($status -ne 'Running') {
              throw "WAS service failed to restart"
            }

            Write-Host "RESULT: WAS restarted successfully"

✅ Notes

  • Manual run only: trigger: none
  • Secure channel: WinRM over HTTPS 5986
  • Safe cleanup: No delete, only rename (timestamped)
  • Audit logs: printed directly to Azure DevOps console

29 Oct 2025

🐧 Linux Shell Scripting for Beginners – Part 1: What is a Variable?πŸ’»

Welcome to this Shell Scripting series! In this first part, we will learn about variables in a very simple and easy-to-understand way.

πŸ“‘ Table of Contents



🌟 What is a Variable?

A variable is like a small box where you store some information. Later, you can open the box and use that information again in your script.

✏️ How to Create a Variable

name="Pradeep"
course="Shell Scripting"
count=10

Important: There should be no space before or after the = sign.

❌ Wrong

name = "Pradeep"

✅ Correct

name="Pradeep"

πŸ“’ How to Use a Variable

echo "Hello $name, welcome to $course training!"
Hello Pradeep, welcome to Shell Scripting training!

🧊 Variables That Cannot Change (readonly)

readonly company="MiddlewareBox"

If you try to change it again, the script will not allow it.


🌍 Global and Local Variables

name="Pradeep"   # Global variable

greet() {
  local name="DevOps Engineer"
  echo "Inside function: $name"
}

greet
echo "Outside function: $name"
Inside function: DevOps Engineer
Outside function: Pradeep

🧠 Store Command Output in a Variable

today=$(date)
echo "Today is: $today"

πŸ’» Simple Practical Example: System Info

host=$(hostname)
os=$(uname -s)
kernel=$(uname -r)

echo "Hostname: $host"
echo "OS: $os"
echo "Kernel: $kernel"

πŸš€ Simple Use Case: App Deployment Path

app="orderservice"
version="v2.3"
path="/opt/apps/$app"

echo "Deploying $app version $version"
mkdir -p $path
cp $app-$version.jar $path/


🧡 Multi-Word Values (Use Quotes)

msg="Hello Shell Scripting Learners"
echo "$msg"

πŸ” Use Variables as Counters

count=1

while [ $count -le 3 ]; do
  echo "Count: $count"
  count=$((count+1))
done

❓ Check if a Variable is Empty

if [ -z "$name" ]; then
  echo "Name is empty"
else
  echo "Name is $name"
fi

⏱️ Use Time in Variable (Backup File Name)

time=$(date +%Y-%m-%d_%H-%M-%S)
file="backup_$time.tar.gz"
echo "Backup file name: $file"

🧰 Small Real-Life Script: Create User and Log

username="john"
log="/var/log/user_creation.log"

echo "Creating user: $username" | tee -a $log
useradd $username
echo "User $username created at $(date)" | tee -a $log

πŸ‘ Quick Summary

TopicMeaning
VariableA box to store data
$variableUse the value
readonlyLock the value
localUse only inside a function
$(command)Save output of a command