Deprecated: Required parameter $post follows optional parameter $content in /customers/3/5/1/about-powershell.com/httpd.www/chen/wp-includes/functions.php on line 840 Warning: Private methods cannot be final as they are never overridden by other classes in /customers/3/5/1/about-powershell.com/httpd.www/chen/wp-includes/class-wp-session-tokens.php on line 69 Deprecated: Required parameter $tt_id follows optional parameter $object_id in /customers/3/5/1/about-powershell.com/httpd.www/chen/wp-includes/nav-menu.php on line 1060 Deprecated: Required parameter $taxonomy follows optional parameter $object_id in /customers/3/5/1/about-powershell.com/httpd.www/chen/wp-includes/nav-menu.php on line 1060 Deprecated: Required parameter $block_attributes follows optional parameter $block_name in /customers/3/5/1/about-powershell.com/httpd.www/chen/wp-includes/blocks.php on line 405 Deprecated: Required parameter $block_content follows optional parameter $block_name in /customers/3/5/1/about-powershell.com/httpd.www/chen/wp-includes/blocks.php on line 405 Warning: Cannot modify header information - headers already sent by (output started at /customers/3/5/1/about-powershell.com/httpd.www/chen/wp-includes/functions.php:840) in /customers/3/5/1/about-powershell.com/httpd.www/chen/wp-includes/feed-rss2.php on line 8 Page not found – Chen V PowerShell Blog https://chen.about-powershell.com I Multiply Happiness by Sharing! Tue, 07 Apr 2020 03:45:10 +0000 en-GB hourly 1 https://wordpress.org/?v=5.5.14 Life Cycle Management: Azure storage account with hirerchial namespace enabled https://chen.about-powershell.com/2020/04/life-cycle-management-azure-storage-account-with-hirerchial-namespace-enabled/ https://chen.about-powershell.com/2020/04/life-cycle-management-azure-storage-account-with-hirerchial-namespace-enabled/#respond Tue, 07 Apr 2020 03:36:04 +0000 http://chen.about-powershell.com/?p=1576

References:

  1. Manage the Azure Blob storage lifecycle
  2. Known issues with Azure Data Lake Storage Gen2

A virtual conversation with a coffee is always my favorite. This morning, I had a chat with my friend in the UK. I was excited to know about an issue in the Azure portal, “Life Cycle Management” settings of a storage account with ADLS Gen 2 (hierarchy namespace enabled) is not appearing. I shared an ARM template way back to the team to set a few properties as part of the cost-saving.

  1. tierttocool
    1. daysAfterModificationGreaterThan = 30
  2. tiertoArchive
    1. daysAfterModificationGreaterThan = 90
  3. delete
    1. daysAfterModificationGreaterThan = 2555

I got a note that the development team couldn’t confirm the solution to close the case. Upon successful execution of the ARM template through Azure pipelines, the result (Life Cycle Management) isn’t visible under blob service.

AFAIK, it’s a glitch in the portal, and PowerShell is the way to confirm. Take a look at the gist of the ARM template to create a storage account with HNS (Hirerchial Namespace) enabled.

{
    "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
    "contentVersion": "1.0.0.0",
    "parameters": {
    },
    "variables": {
    },
    "resources": [
        {
            "name": "[concat('istg003', '/default')]",
            "type": "Microsoft.Storage/storageAccounts/managementPolicies",
            "apiVersion": "2019-04-01",
            "dependsOn": [
                "[resourceId('Microsoft.Storage/storageAccounts' , 'istg003')]"
            ],
            "properties": {
                "policy": {
                    "rules": [
                        {
                            "name": "MyCustomRule-CostSaving",
                            "enabled": true,
                            "type": "Lifecycle",
                            "definition": {
                                "filters": {
                                    "blobTypes": [
                                        "blockBlob"
                                    ]
                                },
                                "actions": {
                                    "baseBlob": {
                                        "tierToCool": {
                                            "daysAfterModificationGreaterThan": 20
                                        },
                                        "tierToArchive": {
                                            "daysAfterModificationGreaterThan": 90
                                        },
                                        "delete": {
                                            "daysAfterModificationGreaterThan": 2555
                                        }
                                    }
                                }
                            }
                        }
                    ]
                }
            }
        },
        {
            "name": "istg003",
            "type": "Microsoft.Storage/storageAccounts",
            "apiVersion": "2019-06-01",
            "location": "[resourceGroup().location]",
            "tags": {
                "displayName": "istg003"
            },
            "properties": {
                "accountType": "STANDARD_RAGRS",
                "isHnsEnabled": true
            },
            "sku": {
                "name": "Standard_RAGRS",
                "tier": "Standard"
            },
            "kind": "StorageV2"
        }
    ],
    "outputs": {
    },
    "functions": [
    ]
}

To check the storage account management policy, use the below snippet

Get-AzStorageAccountManagementPolicy -ResourceGroupName iContoso -StorageAccountName istg003

Output

]]>
https://chen.about-powershell.com/2020/04/life-cycle-management-azure-storage-account-with-hirerchial-namespace-enabled/feed/ 0
Using PowerShell with Azure Functions – Part 1 https://chen.about-powershell.com/2020/04/using-powershell-with-azure-functions-part-1/ https://chen.about-powershell.com/2020/04/using-powershell-with-azure-functions-part-1/#respond Sat, 04 Apr 2020 10:31:19 +0000 http://chen.about-powershell.com/?p=1571

Off late, I built a solution using PowerShell with Azure Functions for simple and complex requirements at my workplace. In this blog series, I would like to cover the most exciting experiences while deploying the solution.  The simple and complex issues are defined based on the environment and use cases, considering that this blog series has no justification for it!

To begin with, let me share a few how-tos!

How to change the port?

If you are running the Azure Functions in the localhost, the port is set to 7071 by default. The below settings allow us to change the port to the desired one.

{
  "IsEncrypted": false,
  "Values": {
    "FUNCTIONS_WORKER_RUNTIME": "powershell",
    "AzureWebJobsStorage": "{AzureWebJobsStorage}"
  },
  "Host": {
    "LocalHttpPort": 8080
  }
}

Is there any alternative approach? Yes, we can start the function using “–port” parameter

func start -port 8081

> Video for your reference!

How to change the route prefix?

Route prefix is nothing but a piece of the string associated with routes by design in the attribute, which is common in the entire controller. For example, look at the URL below where API is the route prefix

http://localhost:7071/api/iGreet

Here is the solution to change the route prefix!

{
  "version": "2.0",
  "managedDependency": {
    "enabled": false
  },
  "extensionBundle": {
    "id": "Microsoft.Azure.Functions.ExtensionBundle",
    "version": "[1.*, 2.0.0)"
  },
  "extensions": {
    "http": {
      "routePrefix": "iAPI"
    }
  }
}

> Video for your reference!

How to extend function time out?

By default, Azure Functions run for 5 minutes, and then timeout occurs. However, it is feasible to extend to 10 minutes—the host.json needs a modification like illustrated below.

{
  "version": "2.0",
  "managedDependency": {
    "enabled": false
  },
  "extensionBundle": {
    "id": "Microsoft.Azure.Functions.ExtensionBundle",
    "version": "[1.*, 2.0.0)"
  },
  "extensions": {
    "http": {
      "routePrefix": "iAPI"
    }
  },
  "functionTimeout": "00:10:00"
}

> Video for your reference!

]]>
https://chen.about-powershell.com/2020/04/using-powershell-with-azure-functions-part-1/feed/ 0
Retrieve Azure Service Updates and Publish as News Letter | PowerShell https://chen.about-powershell.com/2020/01/retrieve-azure-service-updates-and-publish-as-news-letter-powershell/ https://chen.about-powershell.com/2020/01/retrieve-azure-service-updates-and-publish-as-news-letter-powershell/#respond Sun, 26 Jan 2020 14:33:34 +0000 http://chen.about-powershell.com/?p=1566

Credits To: Wriju Ghosh – For sharing the https://azure.microsoft.com/en-us/updates/ and RSS Feed URL https://azurecomcdn.azureedge.net/en-us/updates/feed/

It was a simple ask “How do we know Azure Service Updates?” The answer is to use the link (Azure Service Updates)! But, in this blog post I will show how to retrieve the feed information programmatically, store in Azure Table Storage and send weekly newsletter to business users. Yes, Azure Table Storage will be used to store the service update news once a day (preferably Seattle Local time)

Prerequisites

  • PowerShell
  • Office 365 (Exchange SMTP & Credentials)
  • Azure DevOps (Pipelines)

Solution

Fetch Azure Service Updates

function Get-WeekNumber {
    param (
        [Parameter(Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName)]
        $DateTime
    )

    process {
        $CurrentCulture = [cultureinfo]::CurrentCulture
        $CurrentCulture.Calendar.GetWeekOfYear([datetime]"$($DateTime)", [System.Globalization.CalendarWeekRule]::FirstDay, [System.DayOfWeek]::Monday)
    }
}

Install-Module -Name AzTable -Force -Verbose -Scope CurrentUser
$Config = Get-Content .\config\config.json | ConvertFrom-Json 
Set-AzContext -SubscriptionId $($Config.Subscription) | Out-Null
$StorageContext = New-AzStorageContext -StorageAccountName $($Config.StorageAccountName) -SasToken $($Config.StorageAccountKey)
$CloudTable = (Get-AzStorageTable -Name $($Config.TableName) -Context $($StorageContext)).CloudTable
$AzureUpdates = Invoke-RestMethod -Uri "https://azurecomcdn.azureedge.net/en-us/updates/feed/"
$AzureUpdates | . {
    process {
        Add-AzTableRow -Table $CloudTable -PartitionKey "Microsoft.Azure.Updates.Feed" -RowKey $($_.guid.'#text') -property @{
            WeekNumber    = (Get-WeekNumber -DateTime $_.pubDate)
            Title         = $_.Title
            Category      = $_.Category -split "," -Join ","
            PublishedDate = ([datetime]($_.pubDate)).ToShortDateString()
            ReferenceLink = $_.link
        } -UpdateExisting
    }
}
{
    "Subscription": "SUBCRIPTION ID",
    "StorageAccountName": "STG ACC NAME",
    "TableName": "TABLE NAME",
    "StorageAccountKey": "SECRET KEY",
    "TenantID": "TENANT ID",
    "ClientID": "CLIENT ID",
    "ClientSecret": "CLIENT SECRET",
    "O365Admin": "ADMIN",
    "O365Password": "Password"
}

With no doubt you have to replace the parameter values in config.json. For a demo purpose I used secrets in config file, please plan as per the security standards at your work place.

Now, we need to send consolidated weekly report. The below snippet will help us – Replace the user name and password for O365.

Install-Module -Name AzTable -Force -Verbose -Scope CurrentUser
$Config = Get-Content .\config\config.json | ConvertFrom-Json 
Set-AzContext -SubscriptionId $($Config.Subscription) | Out-Null
$StorageContext = New-AzStorageContext -StorageAccountName $($Config.StorageAccountName) -SasToken $($Config.StorageAccountKey)
$CloudTable = (Get-AzStorageTable -Name $($Config.TableName) -Context $($StorageContext)).CloudTable
$WeekNumber = Get-Date -UFormat %V 
$AzureUpdates = Get-AzTableRow -Table $CloudTable -CustomFilter "(WeekNumber eq $WeekNumber)"

Install-Module PSHTML -Scope CurrentUser -Verbose -Force 
$EmailBody = html -Content {
    head -Content {
    }

    body -Content {
        table -Content {
            table -Content {
                tr -Content {
                    th -Content "Week Number"
                    th -Content "Published Date"
                    th -Content "Title"
                    th -Content "ReferenceLink"
                }
                tr -Content {
                    $AzureUpdates | . {
                        process {
                            tr -Content {
                                td -Content $_.WeekNumber
                                td -Content $_.PublishedDate
                                td -Content $_.Title 
                                td -Content { a -href $($_.ReferenceLink) -Content "Reference Link" }
                            }
                        }
                    }
                }
            }
        }
    }
}

$UserName = $($Config.O365Admin)
$Password = $($Config.O365Password) | ConvertTo-SecureString -AsPlainText -Force 
$Credential = [pscredential]::new($UserName , $Password)
$MailParams = @{
    From       = "AzureServiceUpdate@Microsoft.com"
    To         = "Chendrayan.Exchange@hotmail.com"
    Subject    = "Azure Service Updates - Weekly | Week ($(Get-Date -UFormat %V)) | Year $((Get-Date).Year)"
    SmtpServer = "smtp.office365.com"
    Body       = $EmailBody
    BodyAsHtml = $true
    Port       = 587
    UseSSL     = $true
}

Send-MailMessage @MailParams -Credential $Credential

In my next blog post I will share the Az Pipeline information and other interesting requirements.

 

]]>
https://chen.about-powershell.com/2020/01/retrieve-azure-service-updates-and-publish-as-news-letter-powershell/feed/ 0
Deploy Azure Functions to Azure Function App using ARM (Cross Tenant) https://chen.about-powershell.com/2019/12/deploy-azure-functions-to-azure-function-app-using-arm-cross-tenant/ https://chen.about-powershell.com/2019/12/deploy-azure-functions-to-azure-function-app-using-arm-cross-tenant/#respond Sun, 22 Dec 2019 03:33:42 +0000 http://chen.about-powershell.com/?p=1560

Environment:

  • Azure functions code resides in tenant X (Az DevOps). Azure Cloud is in tenant Y. X and Y tenant’s are restricted to communictae.
  • Source code is in extetnal git and no access to user A (Deployment Engineer).
  • PAT is adviced to use than GIT Credentials (Heard from Security Advisor)

Note: I added only the Az Functions Code SYNC snippet – Not full ARM template (In my next blog I will cover all the steps including the nuances) 

Of late I became a great fan of serverless and worked on few projects at work place using Azure Functions. I got stuck in an Az functions deployment deployment issue because Azure DevOps and Azure Cloud are in two different tenants. I can’t use the deployment center to deploy the solution at ease. Authentication fails!

All the team projects in Azure DevOps are private and has restrictions. Oh well, GitHub enterprise is not an option for me due to other reasons (It’s not in scope of this article). So, what’s next? PAT is my best bet!

Yes, I used Personal Access Token! It’s that simple and git credentials is an alternate! Before showing the ARM template let me show the challenges I faced!

  1. I need to write – yes, write a lot about the deployment steps.
  2. Ensure deployment engineer machine meets pre-requisites.
  3. Unforeseen issue – Timeout during the func azure functionapp publish “myapp” execution.
  4. Function App needs to be deployed manually and functions are published using local GIT / from Azure DevOps.

Manually creating Azure Functions is not a big deal, but I have multiple environments. I want to get rid of manual intervention. No more cribbing right? ? – Developer missed to enable a function, Ops missed to enabled Identity – Those days are gone long back right! ???

With no doubt you can choose any ARM templates – available in web (GitHub, Blogs etc). In “Microsoft.Web/sites” resource add below snippet to get your code as “External Git”

"resources": [
                {
                    "apiVersion": "2018-11-01",
                    "name": "web",
                    "type": "sourcecontrols",
                    "dependsOn": [
                        "[resourceId('Microsoft.Web/Sites/', parameters('functionAppName'))]"
                    ],
                    "properties": {
                        "RepoUrl": "https://anything:{PAT}@ORG.visualstudio.com/TEAM PROJECT/_git/REPOSITORY",
                        "branch": "master",
                        "publishRunbook": true,
                        "IsManualIntegration": true
                    }
                }
            ]

Checkout the REPOURL – It uses a format as shown below

https://anything:{PAT}@ORG.visualstudio.com/TEAM PROJECT/_git/REPOSITORY

You can opt like 

https://PAT:PAT@ORG.visualstudio.com/TEAM PROJECT/_git/REPOSITORY

Note: The below format fails with 401 error – Leads to other confusion! 

https://PAT@ORG.visualstudio.com/TEAM PROJECT/_git/REPOSITORY

Upon success, don’t expect Azure functions to get updated when your source code gets updated – One with permission needs to press SYNC button in deployment center.

In my next blog I will walk through steps to build ARM template which provisions az function apps, get functions from azure devops (as external git). #Serverless #IaC

]]>
https://chen.about-powershell.com/2019/12/deploy-azure-functions-to-azure-function-app-using-arm-cross-tenant/feed/ 0
Retrieve non-compliant virtual machines – Azure Update Management https://chen.about-powershell.com/2019/10/retrieve-non-compliant-virtual-machines-azure-update-management/ https://chen.about-powershell.com/2019/10/retrieve-non-compliant-virtual-machines-azure-update-management/#respond Tue, 22 Oct 2019 12:24:45 +0000 http://chen.about-powershell.com/?p=1544

Team showed me the pain points to pull out the azure virtual machine information which are missing the security and critical updates. I tried my best and found a way to pull the report using PowerShell. Indeed, there can be alternative – But, this solution gave us the data we need!

Code

param (
    $SUBSCRIPTIONID,

    $AUTOMATIONACCOUNTNAME,

    $RESOURCEGROUPNAME,

    $WORKSPACE
)
#region - Generate a bearer token
$azureRmProfile = [Microsoft.Azure.Commands.Common.Authentication.Abstractions.AzureRmProfileProvider]::Instance.Profile
$currentAzureContext = Get-AzContext
$profileClient = New-Object Microsoft.Azure.Commands.ResourceManager.Common.RMProfileClient($azureRmProfile)
$token = $profileClient.AcquireAccessToken($currentAzureContext.Subscription.TenantId)
#endregion


$Query = @"
{
    "top": 1000000000,
    "query": "Heartbeat\n| where TimeGenerated>ago(12h) and OSType==\"Linux\" and notempty(Computer) | where ComputerEnvironment=~\"Azure\"\n| summarize arg_max(TimeGenerated, Solutions, Computer, ResourceId, ComputerEnvironment, VMUUID) by SourceComputerId\n| where Solutions has \"updates\" \n| extend vmuuId=VMUUID, azureResourceId=ResourceId, osType=1,\nenvironment=iff(ComputerEnvironment=~\"Azure\", 1, 2),\nscopedToUpdatesSolution=true, lastUpdateAgentSeenTime=\"\"\n| join kind=leftouter (Update\n| where TimeGenerated>ago(5h) and OSType==\"Linux\" and SourceComputerId in ((Heartbeat\n| where TimeGenerated>ago(12h) and OSType==\"Linux\" and notempty(Computer)\n| summarize arg_max(TimeGenerated, Solutions) by SourceComputerId\n| where Solutions has \"updates\" | distinct SourceComputerId)) | where ComputerEnvironment=~\"Azure\"\n| summarize hint.strategy=partitioned arg_max(TimeGenerated, UpdateState, Classification, Product, Computer, ComputerEnvironment) by SourceComputerId, Product, ProductArch \n| summarize Computer=any(Computer), ComputerEnvironment=any(ComputerEnvironment), missingCriticalUpdatesCount=countif(Classification has \"Critical\" and UpdateState=~\"Needed\"),\nmissingSecurityUpdatesCount=countif(Classification has \"Security\" and UpdateState=~\"Needed\"),\nmissingOtherUpdatesCount=countif(Classification !has \"Critical\" and Classification !has \"Security\" and UpdateState=~\"Needed\"),\nlastAssessedTime=max(TimeGenerated), lastUpdateAgentSeenTime=\"\" by SourceComputerId\n| extend compliance=iff(missingCriticalUpdatesCount > 0 or missingSecurityUpdatesCount > 0, 2, 1)\n| extend ComplianceOrder=iff(missingCriticalUpdatesCount > 0 or missingSecurityUpdatesCount > 0 or missingOtherUpdatesCount > 0, 1, 3)) on SourceComputerId\n| project id=SourceComputerId, displayName=Computer ,sourceComputerId=SourceComputerId, azureResourceId, scopedToUpdatesSolution=true,\nmissingCriticalUpdatesCount=coalesce(missingCriticalUpdatesCount, -1), missingSecurityUpdatesCount=coalesce(missingSecurityUpdatesCount, -1), missingOtherUpdatesCount=coalesce(missingOtherUpdatesCount, -1), compliance=coalesce(compliance, 4), lastAssessedTime, lastUpdateAgentSeenTime, osType=1, environment=iff(ComputerEnvironment=~\"Azure\", 1, 2), ComplianceOrder=coalesce(ComplianceOrder, 2)\n | where compliance in (2) | union(Heartbeat\n| where TimeGenerated>ago(12h) and OSType=~\"Windows\" and notempty(Computer) | where ComputerEnvironment=~\"Azure\"\n| summarize arg_max(TimeGenerated, Solutions, Computer, ResourceId, ComputerEnvironment, VMUUID) by SourceComputerId\n| where Solutions has \"updates\" \n| extend vmuuId=VMUUID, azureResourceId=ResourceId, osType=2,\nenvironment=iff(ComputerEnvironment=~\"Azure\", 1, 2),\nscopedToUpdatesSolution=true, lastUpdateAgentSeenTime=\"\"\n| join kind=leftouter (Update\n| where TimeGenerated>ago(14h) and OSType!=\"Linux\" and SourceComputerId in ((Heartbeat\n| where TimeGenerated>ago(12h) and OSType=~\"Windows\" and notempty(Computer)\n| summarize arg_max(TimeGenerated, Solutions) by SourceComputerId\n| where Solutions has \"updates\" | distinct SourceComputerId)) | where ComputerEnvironment=~\"Azure\"\n| summarize hint.strategy=partitioned arg_max(TimeGenerated, UpdateState, Classification, Title, Optional, Approved, Computer, ComputerEnvironment) by Computer, SourceComputerId, UpdateID \n| summarize Computer=any(Computer), ComputerEnvironment=any(ComputerEnvironment), missingCriticalUpdatesCount=countif(Classification has \"Critical\" and UpdateState=~\"Needed\" and Approved!=false),\nmissingSecurityUpdatesCount=countif(Classification has \"Security\" and UpdateState=~\"Needed\" and Approved!=false),\nmissingOtherUpdatesCount=countif(Classification !has \"Critical\" and Classification !has \"Security\" and UpdateState=~\"Needed\" and Optional==false and Approved!=false),\nlastAssessedTime=max(TimeGenerated), lastUpdateAgentSeenTime=\"\" by SourceComputerId\n| extend compliance=iff(missingCriticalUpdatesCount > 0 or missingSecurityUpdatesCount > 0, 2, 1)\n| extend ComplianceOrder=iff(missingCriticalUpdatesCount > 0 or missingSecurityUpdatesCount > 0 or missingOtherUpdatesCount > 0, 1, 3)) on SourceComputerId\n| project id=SourceComputerId, displayName=Computer, sourceComputerId=SourceComputerId, azureResourceId, scopedToUpdatesSolution=true,\nmissingCriticalUpdatesCount=coalesce(missingCriticalUpdatesCount, -1), missingSecurityUpdatesCount=coalesce(missingSecurityUpdatesCount, -1), missingOtherUpdatesCount=coalesce(missingOtherUpdatesCount, -1), compliance=coalesce(compliance, 4), lastAssessedTime, lastUpdateAgentSeenTime, osType=2, environment=iff(ComputerEnvironment=~\"Azure\", 1, 2), ComplianceOrder=coalesce(ComplianceOrder, 2)\n | where compliance in (2)) | order by ComplianceOrder asc, missingCriticalUpdatesCount desc, missingSecurityUpdatesCount desc, missingOtherUpdatesCount desc, displayName asc"
}
"@ 


$result = Invoke-RestMethod -Uri "https://management.azure.com/subscriptions/$($SUBSCRIPTIONID)/resourcegroups/$($RESOURCEGROUPNAME)/microsoft.operationalinsights/workspaces/$($WORKSPACE)/query?api-version=2017-10-01&q_OrchestratorExtension.DataModels.Computer" -Headers @{
    Authorization = "Bearer {0}" -f ($token.AccessToken)
} -Method Post -Body ($Query) -ContentType 'application/json'

$Collection = @()
$result.tables.rows  | %{
    $Collection += [pscustomobject]@{
        VMName = $_[1]
        CriticalUpdateMissing = $_[5]
        SecurityUpdateMissing = $_[6]

    }
} 
$Collection

Try it! Let me know if you have alternate solution!

]]>
https://chen.about-powershell.com/2019/10/retrieve-non-compliant-virtual-machines-azure-update-management/feed/ 0
AZ CLI (Resource Graph) – To retrieve azure virtual machine information with power status. https://chen.about-powershell.com/2019/08/az-cli-resource-graph-to-retrieve-azure-virtual-machine-information-with-power-status/ https://chen.about-powershell.com/2019/08/az-cli-resource-graph-to-retrieve-azure-virtual-machine-information-with-power-status/#respond Wed, 28 Aug 2019 10:58:51 +0000 http://chen.about-powershell.com/?p=1524

I was developing a solution to fetch azure VM information as part of the update management process. Indeed, there are many ways to pull the reports. But, I thought of doing it in az cli is best options! Here is the script which pulls out the azure virtual machines information with power status.

$virtualMachines = az.cmd graph query -q "where type =~ 'Microsoft.Compute/virtualMachines' | extend aliases | project id, sku = tostring(aliases['Microsoft.Compute/imageSku']), offer = tostring(aliases['Microsoft.Compute/imageOffer']) , publisher = tostring(aliases['Microsoft.Compute/imagePublisher']) | where offer == 'WindowsServer'" --first 200 | ConvertFrom-Json
$groupBy = 40
$resultCollection = @()
$group = [math]::Ceiling($virtualMachines.Count / $groupBy)
for ($i = 0; $i -le $group; $i++)
{
    $start = $i * $groupBy
    $end = (($i + 1) * $groupBy) - 1
    "Processing for $($start) .. $($end)"
    $resultCollection += @(az.cmd vm get-instance-view --ids $($virtualMachines[$start..$end].id)) | ConvertFrom-Json
}
$resultCollection

That’s the first cut of scripts which worked as expected – There are some good and bad in the preceding code.

Good: It just works!

Bad: It’s ugly

Working on alternatives!

 

 

]]>
https://chen.about-powershell.com/2019/08/az-cli-resource-graph-to-retrieve-azure-virtual-machine-information-with-power-status/feed/ 0
Microsoft Security Response Center (MSRC) Query and Parse a REST API with PowerShell https://chen.about-powershell.com/2019/07/microsoft-security-response-center-msrc-query-and-parse-a-rest-api-with-powershell/ https://chen.about-powershell.com/2019/07/microsoft-security-response-center-msrc-query-and-parse-a-rest-api-with-powershell/#respond Tue, 23 Jul 2019 09:22:30 +0000 http://chen.about-powershell.com/?p=1516

I got a requirement which is to fetch MSRC report. If you are new to Microsoft Security Center API like me refer here. Using your Microsoft ID generate an api key.

The ask is very simple – Retrieve the report which should contain below information

  1. CVE Title.
  2. CVE Number.
  3. Published Date.
  4. Affected Products. (This gives information what we need)

Indeed, team came up with the MsrcSecurityUpdates PowerShell module which is available in the gallery. But we don’t use that – Our need is to fetch report which meets old MS vulnerability report format.

So, the REST API is our friend and below simple script meet the need.

$monthofinterest = @('2017-Apr' , 
    '2017-May', 
    '2017-Jun', 
    '2017-Jul', 
    '2017-Aug', 
    '2017-Sep', 
    '2017-Oct', 
    '2017-Nov', 
    '2017-Dec', 
    '2018-Jan', 
    '2018-Feb', 
    '2018-Mar', 
    '2018-Apr', 
    '2018-May', 
    '2018-Jun', 
    '2018-Jul', 
    '2018-Aug', 
    '2018-Sep', 
    '2018-Oct', 
    '2018-Nov', 
    '2018-Dec', 
    '2019-Jan', 
    '2019-Feb', 
    '2019-Mar', 
    '2019-Apr', 
    '2019-May', 
    '2019-Jun', 
    '2019-Jul'  )
$colls = @()
$monthofinterest | . {
    process
    {
        $pcdReport = Invoke-RestMethod -Method Get -Uri "https://api.msrc.microsoft.com/cvrf/$($_)?api-version=2018" -Headers @{
            'api-key' = '<APIKey>'
        }
        $pcdReport.cvrfdoc.Vulnerability.cve | . {
            process
            {
                $results = Invoke-RestMethod -Uri "https://portal.msrc.microsoft.com/api/security-guidance/en-US/CVE/$($_)"
                $results
                $colls += $results
            }
        }
    }
}
$colls | 
Select-Object cve* -ExpandProperty affectedproducts | 
Export-Csv C:\Temp\MSRCRawReport.csv -NoTypeInformation

Refer this blog post if you need data in different format.

]]>
https://chen.about-powershell.com/2019/07/microsoft-security-response-center-msrc-query-and-parse-a-rest-api-with-powershell/feed/ 0
Windows Subsystem For Linux and Remote Development (Remote-WSL) https://chen.about-powershell.com/2019/06/windows-subsystem-for-linux-and-remote-development-remote-wsl/ https://chen.about-powershell.com/2019/06/windows-subsystem-for-linux-and-remote-development-remote-wsl/#respond Sun, 30 Jun 2019 04:25:40 +0000 http://chen.about-powershell.com/?p=1495 June 29, 2018 – BITPro Meetup held at LinkedIn India office which helped me to understand two main things #WSL (Windows Subsystem For Linux) and #Remote-WSL (VSCode Extension) . Like many I was aware of it but never had a chance to work. So, I thought of trying it once and now I couldn’t stop.  Let me share what I learnt.

About WSL 2

Install WSL on Windows 10

Yes, it’s straight forward approach and documentation is self-explanatory. While doing the installation @ravikanth showed me how to do remote development on WSL using VSCode. Indeed, that made me to explore the WSL a bit more. Again, it’s very simple – Just install remote development extension on VSCode which includes REMOTE – SSH , REMOTE – CONTAINERS , REMOTE – WSL.

REMOTE DEVELOPMENT – VSCode

What I observed is no intellisense support in Remote Development atleast for PowerShell. Anyways, here is a quick Hello World Web Server running on WSL as a sandbox – Accessible from Windows 10 🙂 🙂 🙂

]]>
https://chen.about-powershell.com/2019/06/windows-subsystem-for-linux-and-remote-development-remote-wsl/feed/ 0
Working with Azure Function App using PowerShell and PSHTML module – Part 3 https://chen.about-powershell.com/2019/05/working-with-azure-function-app-using-powershell-and-pshtml-module-part-3/ https://chen.about-powershell.com/2019/05/working-with-azure-function-app-using-powershell-and-pshtml-module-part-3/#comments Sun, 05 May 2019 10:11:56 +0000 http://chen.about-powershell.com/?p=1474 In this blog post I will show a simple method to send email to the given address (Input form with a email input field and a submit button). Yes, you can think alternate for Office365 SMTP like send grid and mail jet services. But the ASKS is to send email with HTML body to given mail address. So, I opted for existing O365 to save time!

Output

Simple steps are as follows

  1. Add your O365 user name and password in key vaults.
  2. Add the key vaults secrets in your functions app settings.
  3. Just follow the documentation for Azure Functions—Key Vault integration
  4. Create two functions with HTTP triggers

iAbout

using namespace System.Net
param($Request, $TriggerMetadata)
$html = html -Content {
    head -Content {
        link -rel "stylesheet" -href "https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" -integrity "sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" -crossorigin "anonymous"
        link -rel "stylesheet" -href "https://cdn.metroui.org.ua/v4/css/metro-all.min.css"
        link -rel "stylesheet" -href "https://cdn.metroui.org.ua/v4/css/metro.min.css"
        link -rel "stylesheet" -href "https://cdn.metroui.org.ua/v4/css/metro-colors.min.css"
        link -rel "stylesheet" -href "https://cdn.metroui.org.ua/v4/css/metro-rtl.min.css"
        link -rel "stylesheet" -href "https://cdn.metroui.org.ua/v4/css/metro-icons.min.css"
        script -src "https://code.jquery.com/jquery-3.3.1.min.js"
        script -src "https://cdn.metroui.org.ua/v4/js/metro.min.js"
        script -src "https://code.jquery.com/jquery-3.2.1.slim.min.js" -integrity "sha384-KJ3o2DKtIkvYIK3UENzmM7KCkRr/rE9/Qpg6aAZGJwFDMVNA/GpGFF93hXpG5KkN" -crossorigin "anonymous"
        script -src "https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js" -integrity "sha384-ApNbgh9B+Y1QKtv3Rn7W3mgPxhU9K/ScQsAP7hUibX39j7fakFPskvXusvfa0b4Q" -crossorigin "anonymous"
        script -src "https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js" -integrity "sha384-JZR6Spejh4U02d8jOt6vLEHfe/JQGiRRSQQxSfFWpi1MquVdAyjUar5+76PVCmYl" -crossorigin "anonymous"
        title -Content "iContact"
    }
    body -Content {
        div -Class 'jumbotron' -Content {
            h1 -Class 'display-4' -Content "O365 Email..."
        }
        form -action '/api/iSendEmail' -method 'post' -enctype 'application/x-www-form-urlencoded' -target _blank -Content {
            div -Class 'form-group' -Content {
                input -type email -name email -Class 'form-control'
            }
            button -Attributes @{type = 'submit' } -Class 'btn btn-primary' -Content 'Send Email'
        }
    }
}
Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{
        headers    = @{'content-type' = 'text/html' }
        StatusCode = [HttpStatusCode]::OK
        Body       = $html
    })

iSendEmail

using namespace System.Net;
using namespace System.Web;

param($Request, $TriggerMetadata)

$formdata = ([ordered]@{ })
$DecodedBody = [System.Web.HttpUtility]::UrlDecode($Request.Body)
($($DecodedBody) -split "&").ForEach( { $value = $_.split("="); $formdata.Add($value[0], $value[1]) })

$O365UserName = [System.Environment]::GetEnvironmentVariable('O365AdminUserName')
$O365Password = [System.Environment]::GetEnvironmentVariable('O365AdminPassword') | ConvertTo-SecureString -AsPlainText -Force
$Credential = [pscredential]::new($O365UserName, $O365Password)

$Body | ConvertTo-Html
$MailParams = @{
    From       = 'chendrayan@chensoffice365.onmicrosoft.com'
    To         = $($formdata.email)
    Subject    = 'Test mail from Az Function App...'
    Body       = @"
  Hi Chen V,
  
    This is a test mail from Azure Function app using the Office 365 SMTP server. 

  Regards,
  Chen V
"@
    SMTPServer = 'smtp.office365.com'
    Usessl     = $true
    Credential = $Credential
}

Send-MailMessage @MailParams 

Push-OutputBinding -name Response -Value ([HttpResponseContext]@{
        headers    = @{'content-type' = 'application/json' }
        StatusCode = [HttpStatusCode]::OK
        Body       = [pscustomobject]@{
            formdata = $formdata
            UserName = $O365UserName
            Message  = "Message Sent from O365"
        } | ConvertTo-Json
    })

Enjoy PowerShell – In my next blog we will discuss and demo the event driven automation using functions app with PowerShell.

]]>
https://chen.about-powershell.com/2019/05/working-with-azure-function-app-using-powershell-and-pshtml-module-part-3/feed/ 2
Working with Azure Function App using PowerShell and PSHTML module – Part 2 https://chen.about-powershell.com/2019/05/working-with-azure-function-app-using-powershell-and-pshtml-module-part-2/ https://chen.about-powershell.com/2019/05/working-with-azure-function-app-using-powershell-and-pshtml-module-part-2/#respond Wed, 01 May 2019 07:52:54 +0000 http://chen.about-powershell.com/?p=1463

In my previous blog post I demoed a simple example – GET and POST in Azure Function App. Here, comes the second part of it which answers the below questions (ASKS from readers)

  1. How to upload required modules in Azure Function App?
  2. How to beautify my web app with no programming skills? I am an IT Pro I just need a serverless portal (UI/UX) just for a click through and automate infrastructure.

Worth to read the documentation for creating your first azure functions using PowerShell. Assuming your environment setup is ready to follow the instructions and you should be here now!

Create a folder and name it is as modules – Upload all the required PowerShell modules here and azure functions imports with no additional line of snippet. Yes, no need to use Import-Module , Install-Module etc. We are building a serverless web application – So, I need PSHTML PowerShell module which I copied from my local machine

Now, its time for us to create a PowerShell function and test it locally – Here is the quick start!

After a successful test in local machine – Publish it in Azure Functions and below snippet in az func core tools will do that!

func azure functionapp publish iAutomate

Use –force if required

So, we now know how to use the modules in Az Functions App – Let me show using the bootstrap in PSHTML to beautify your web application. It’s very simple to use CDN 🙂

The below four lines of the snippet does the magic for us and you can add more than one number of styling and scripts.

head -Content {
    link -rel "stylesheet" -href "https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" -integrity "sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" -crossorigin "anonymous"
    script -src "https://code.jquery.com/jquery-3.2.1.slim.min.js" -integrity "sha384-KJ3o2DKtIkvYIK3UENzmM7KCkRr/rE9/Qpg6aAZGJwFDMVNA/GpGFF93hXpG5KkN" -crossorigin "anonymous"
    script -src "https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js" -integrity "sha384-ApNbgh9B+Y1QKtv3Rn7W3mgPxhU9K/ScQsAP7hUibX39j7fakFPskvXusvfa0b4Q" -crossorigin "anonymous"
    script -src "https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js" -integrity "sha384-JZR6Spejh4U02d8jOt6vLEHfe/JQGiRRSQQxSfFWpi1MquVdAyjUar5+76PVCmYl" -crossorigin "anonymous"
    title -Content "iHome"
}

Now see the result of Jumbotron! (Landing Page)

Here is the full code!

using namespace System.Net
param($Request, $TriggerMetadata)
$html = html -Content {
    head -Content {
        link -rel "stylesheet" -href "https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" -integrity "sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" -crossorigin "anonymous"
        script -src "https://code.jquery.com/jquery-3.2.1.slim.min.js" -integrity "sha384-KJ3o2DKtIkvYIK3UENzmM7KCkRr/rE9/Qpg6aAZGJwFDMVNA/GpGFF93hXpG5KkN" -crossorigin "anonymous"
        script -src "https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js" -integrity "sha384-ApNbgh9B+Y1QKtv3Rn7W3mgPxhU9K/ScQsAP7hUibX39j7fakFPskvXusvfa0b4Q" -crossorigin "anonymous"
        script -src "https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js" -integrity "sha384-JZR6Spejh4U02d8jOt6vLEHfe/JQGiRRSQQxSfFWpi1MquVdAyjUar5+76PVCmYl" -crossorigin "anonymous"
        title -Content "iHome"
    }
    body -Content {
        div -Class 'jumbotron' -Content {
            h1 -Class 'display-4' -Content 'iHome'
            p -Class 'lead' -Content {
                "This is a Azure Function App built using PowerShell..."
                p -Content {
                    "Only PowerShell and nothing else... CSS and JS are from CDN!"
                } -Style 'font-style:italic;font-weight:bold'
            }
            hr -Class 'my-1'
            p -Class 'lead' -Content {
                "PowerShell &hearts; Azure Functions App"
            } -style 'text-align:center;font-weight:bold'
        }
    }
}
Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{
        headers    = @{'content-type' = 'text/html' }
        StatusCode = [HttpStatusCode]::OK
        Body       = $html
    })

In my next blog post I will demo more PSHTML and event driven automation.

]]>
https://chen.about-powershell.com/2019/05/working-with-azure-function-app-using-powershell-and-pshtml-module-part-2/feed/ 0