In Terraform, how to pass variables to powershell template file? - powershell

I am creating a windows EC2 instance using Terraform. I am setting powershell script to user_data that will run after the instance is launched.
resource "aws_instance" "windows_runner" {
ami = var.ami_id
instance_type = var.instance_type
iam_instance_profile = aws_iam_instance_profile.runner_instance.name
key_name = var.key_name
security_groups = [aws_security_group.windows.id]
subnet_id = data.aws_subnet.work.id
tags = var.tags
user_data = teamplatefile("userdata.tftpl",
{
environment_name = var.environment_name,
instance_type = var.instance_type
})
}
So I am passing these two variables to templatefile function, and I want to consume those two variables in powershell script. Based on the [documentation][1]
The "vars" argument must be a map. Within the template file, each of
the keys in the map is available as a variable for interpolation
Here is how userdata.tftpl will consume those variables
<powershell>
Set-Location -Path "C:\GitLab-Runner"
$token = "sometoken"
$url = "https://gitlab-instance.domain.com/"
$runner_name = "windows-runner-$instance_type"
$executor = "shell"
$shell = "powershell"
$builds_location = "c:\builds"
$tags = "$environment_name-windows-$instance_type"
New-Item -Path $builds_location -ItemType Directory -ErrorAction SilentlyContinue
Write-Host "$builds_location has been created!"
# register runner. Trailing backtick to span on multiple line. White space matters
.\gitlab-runner.exe register `
--non-interactive `
--url $url `
--registration-token $token `
--name $runner_name `
--executor $executor `
--shell $shell `
--builds-dir $builds_location `
--tag-list $tags `
--locked="false"
.\gitlab-runner.exe install
.\gitlab-runner.exe start
</powershell>
Questions
1> How these variables will be available in powershell?
2> Can I simply use $environment_name and $instance_type in PS script?
3> or will these variables passed as parameters to the PS script? In this case do I need to define and match the parameter names or order?
4>Will the PS script execute in Adminstyrator mode on launched instance?
Bonus
I found examples using templatefile on linux and shell script. Not finding a good example with windows and powershell
[1]: https://developer.hashicorp.com/terraform/language/functions/templatefile

Related

Azure Function app won't load dependencies

I have checked the requirements.psd1 several times and it all appears right, but the function returns this error when running.
[Warning] The Function app may be missing a module containing the 'Set-AzStorageBlobContent' command definition. If this command belongs to a module available on the PowerShell Gallery, add a reference to this module to requirements.psd1. Make sure this module is compatible with PowerShell 7. For more details, see https://aka.ms/functions-powershell-managed-dependency. If the module is installed but you are still getting this error, try to import the module explicitly by invoking Import-Module just before the command that produces the error: this will not fix the issue but will expose the root cause.
2022-09-13T22:12:00.401 [Error] ERROR: The term 'Set-AzStorageBlobContent' is not recognized as the name of a cmdlet, function, script file, or operable program.Check the spelling of the name, or if a path was included, verify that the path is correct and try again.Exception :Type : System.Management.Automation.CommandNotFoundExceptionErrorRecord :Exception :Type : System.Management.Automation.ParentContainsErrorRecordExceptionMessage : The term 'Set-AzStorageBlobContent' is not recognized as the name of a cmdlet, function, script file, or operable program.Check the spelling of the name, or if a path was included, verify that the path is correct and try again.HResult : -2146233087TargetObject : Set-AzStorageBlobContentCategoryInfo : ObjectNotFound: (Set-AzStorageBlobContent:String)
I am not sure what i'm missing. I've read through other fixes i've found and believe I have it configured correctly. It feels "buggy". Here's my config below:
function.json
{
"bindings": [
{
"name": "Timer",
"type": "timerTrigger",
"direction": "in",
"schedule": "0 * * * * *"
}
]
}
host.json
{
"version": "2.0",
"managedDependency": {
"Enabled": true
},
"extensionBundle": {
"id": "Microsoft.Azure.Functions.ExtensionBundle",
"version": "[2.*, 3.0.0)"
}
}
profile.ps1
#if ($env:MSI_SECRET) {
# Disable-AzContextAutosave -Scope Process | Out-Null
# Connect-AzAccount -Identity
#}
requirements.psd1
# This file enables modules to be automatically managed by the Functions service.
# See https://aka.ms/functionsmanageddependency for additional information.
#
#{
# For latest supported version, go to 'https://www.powershellgallery.com/packages/Az'.
# To use the Az module in your function app, please uncomment the line below.
'Az' = '7.*'
'Az.KeyVault' = '4.*'
'Az.Storage' = '4.*'
}
and the script that's running
#---------------------------------------------------------[Variables]------------------------------------------------------------
$storageAccountName = 'storageaccount'
$containerName = '$web'
$logContainerName = 'logfiles'
$subscription = 'Subscription'
$resourceGroupName = 'resourcegroup'
$blob = 'info.txt'
$logBlob = 'info.log'
$uri = "https://api.binaryedge.io/v1/minions"
#----------------------------------------------------------[Execution]-------------------------------------------------------------
# Call the API to get the IP addresses
Try {
$call = Invoke-RestMethod $uri -ErrorAction Stop
$list = $call.scanners
# New-TemporaryFile uses [System.IO.Path]::GetTempPath() location
$tempFile = New-TemporaryFile
# Set the context to the subscription you want to use
# If your functionApp has access to more than one subscription it will load the first subscription by default.
Set-AzContext -Subscription $subscription
# Get the Storage Account Key to authenticate
$storAccKeys = Get-AzStorageAccountKey -ResourceGroupName $resourceGroupName -Name $storageAccountName
$primaryKey = $storAccKeys | Where-Object keyname -eq 'key1' | Select-Object -ExpandProperty value
# Write the CIDR list to the temp file created earlier
$list | Out-File $tempFile
# Create a Storage Context which will be used in the subsequent commands
$storageContext = New-AzStorageContext -StorageAccountName $storageAccountName -StorageAccountKey $primaryKey
# Upload the temp file to blob storage
$setAzStorageBlobContentSplat = #{
Container = $containerName
File = $tempFile.FullName
Blob = $blob
Context = $storageContext
Properties = #{
ContentType = 'text/plain'
}
}
Set-AzStorageBlobContent #setAzStorageBlobContentSplat -Force
Write-Host Success!
}
Catch {
Out-Host $_.exception.message
}
Finally {
$time = Get-Date
$tempFile = New-TemporaryFile
"Script last completed at $time" | Out-File $tempFile -Append
$setAzStorageBlobContentSplat = #{
Container = $logContainerName
File = $tempFile.FullName
Blob = $logBlob
Context = $storageContext
Properties = #{
ContentType = 'text/plain'
}
}
Set-AzStorageBlobContent #setAzStorageBlobContentSplat -Force
}
Figured it out. For whatever reason, Connect-AzAccount is now required. Since I used a managed identity, the command to allow the script to run is Connect-AzAccount -Identity. You also need to add Az.Accounts to the requirements.psd1. This was the fix.
In the profile.ps1 file, I also had to uncomment the lines. This is the default, but I did it to get rid of one of the errors.
I will state for the record...several months ago this was NOT a requirement to make a script run. It also wasn't working in the profiles.ps1 file prior to commenting it out either.
I would recommend following the suggestion in the warning message and invoking Import-Module Az.Storage just before Set-AzStorageBlobContent. This will probably not fix the issue, but Import-Module will tell you why it cannot load the module.

How can I use pester v5 configuration or container with four standard arguments?

I'm trying to invoke a pester script, moving from pester V4.6.0 to V5.3.1
The V4 arguments I used to use, when invoking pester were:
-supplying custom parameters
-OutputFormat NUnitXML
-OutputFile $xmlpath
-EnableExit
However, it seems for v5, I need to invoke Pester using either a configuration OR a container, neither of which cater fully for the arguments I used in V4. The limitations seem to be:
Invoke-Pester -Configuration $pesterConfig
where $pesterConfig doesn't like me specifying custom data params in it, eg:
Data = #{dataFactoryName = $dataFactoryName;}
or
Invoke-Pester -Container $container -ErrorAction Stop -Output Detailed -CI
where $container doesn't like me specifying:
-OutputFormat NUnitXML
-OutputFile $xmlpath
-EnableExit
How can I more efficiently use either container or configuration, to allow me to specify the following 4 things:
-supplying custom parameters
-OutputFormat NUnitXML
-OutputFile $xmlpath
-EnableExit
One work around, would be to settle for using configuration, which caters for 3 of my 4 arguments, and pass custom data to a file or env variable and read it in within the invoked ps script. Probably not ideal though.
Found a way to use configuration and container together:
$container = New-PesterContainer -Path "..." -Data #{
customParamA= "value";
customParamB= "value";
}
$pesterConfig = [PesterConfiguration]#{
Run = #{
Exit = $true
Container = $container
}
Output = #{
Verbosity = 'Detailed'
}
TestResult = #{
Enabled = $true
OutputFormat = "NUnitXml"
OutputPath = $xmlpath
}
Should = #{
ErrorAction = 'Stop'
}
}
Invoke-Pester -Configuration $pesterConfig

Output Azure Function powershell value to azure storage account

I have a powershell script running in an azure function app that grabs a json list of IPs, parses it and just returns a list of IPs with 1 per line. I need to take this output and save it to a specific azure storage account. I'm attempting to use the static website in the storage account to create an HTTP endpoint for others to retrieve the list of IPs.
Here's the script
# Input bindings are passed in via param block.
param($Timer)
# Get the current universal time in the default string format.
$currentUTCtime = (Get-Date).ToUniversalTime()
# The 'IsPastDue' property is 'true' when the current function invocation is later than scheduled.
if ($Timer.IsPastDue) {
Write-Host "PowerShell timer is running late!"
}
# Write an information log with the current time.
Write-Host "PowerShell timer trigger function ran! TIME: $currentUTCtime"
#-------------------------------------------------------------------------------------------
$url = "https://ip-ranges.atlassian.com/"
Invoke-RestMethod $url
$iplist = Invoke-RestMethod $url
$iplist.items | select-object -ExpandProperty cidr
out-file $outputBlob
I've tested the function in azure and it runs there just fine. I can't seem to get the integration outputs section of the function app to work. The settings for the outputs is
Binding type - azure blob storage
blob paramter name - outputBlob
Path - test/list.txt
storage account connection - searched and selected the storage account
I am not finding much documentation on how to make my powershell script output to this storage account. The out-file clearly doesn't work.
----------- updated code ----------
Here is the code that now successfully saves the file to a container, but I still cant save to the $web container for the static website. The $ is not something I can use in the output binding
# Input bindings are passed in via param block.
param($Timer)
# Get the current universal time in the default string format.
$currentUTCtime = (Get-Date).ToUniversalTime()
# The 'IsPastDue' property is 'true' when the current function invocation is later than scheduled.
if ($Timer.IsPastDue) {
Write-Host "PowerShell timer is running late!"
}
# Write an information log with the current time.
Write-Host "PowerShell timer trigger function ran! TIME: $currentUTCtime"
#------------------------
$url = "https://ip-ranges.atlassian.com/"
Invoke-RestMethod $url
$call = Invoke-RestMethod $url
$iplist = $call.items | select-object -ExpandProperty cidr
$output = out-string -InputObject $iplist
Push-OutputBinding -Name outputBlob -Value $output
the outputBlob binding is configured under integration > outputs > and the Path which is in the format of container/file. I cannot specify $web/file.txt...but if I do web/file.txt it will create a web container and put the output as file.txt within it. I need to do this, but it must go in the $web container.
This is something that I've been meaning to try for a little while but not actually got around to. Decided to give it a shot today when I seen your question.
So it is possible to push content using the blob output binding but the functionality is limited.
run.ps1
using namespace System.Net
# Input bindings are passed in via param block.
param (
$Request,
$TriggerMetadata
)
# Call the atlassian API to get the address ranges
$atlassianUri = "https://ip-ranges.atlassian.com/"
$iplist = Invoke-RestMethod $atlassianUri -ErrorAction Stop
$cidrList = $iplist.items | select-object -ExpandProperty cidr
# Push the contents to blob storage using the outputBinding
Push-OutputBinding -Name myOutputBlob -Value $cidrList
# Return a simple response so I know it worked
Push-OutputBinding -Name Response -Value ([HttpResponseContext]#{
StatusCode = [HttpStatusCode]::OK
Body = 'Successfully Updated Blob Storage'
})
function.json
You would have to include an timer Input Binding in your function but I used HTTP so that I could trigger it on-demand to test that it would work.
I have provided a static path to the blob output binding. The Path property can not be dynamically assigned from within the function yet according to this open GitHub issue.
{
"bindings": [
{
"authLevel": "function",
"type": "httpTrigger",
"direction": "in",
"name": "Request",
"methods": [
"get",
"post"
]
},
{
"type": "http",
"direction": "out",
"name": "Response"
},
{
"name": "myOutputBlob",
"type": "blob",
"path": "functioncopy/ipRanges.txt",
"connection": "MyStorageConnectionAppSetting",
"direction": "out"
}
]
}
The above code works however when sending the data to the blob file in the storage account Push-OutputBinding serialises the content to a JSON array e.g.
This may or may not work for you in it's current guise but I don't think there is a way using the output binding to just have a raw list.
You could however use the Az.Storage module within your function, create the file within your function execution and upload it that way instead
run.ps1
# Variables required - Fill these out
$storageAccountName = '<Insert Storage Account Here'
$containerName = '<Insert StorageContainer Name Here>'
$resourceGroupName = '<Insert resourceGroup Name Here>'
$subscriptionId = '<Insert subscriptionId Here>'
# Call the atlassian API to get the address ranges
$atlassianUri = "https://ip-ranges.atlassian.com/"
$iplist = Invoke-RestMethod $atlassianUri -ErrorAction Stop
$cidrList = $iplist.items | select-object -ExpandProperty cidr
# New-TemporaryFile uses [System.IO.Path]::GetTempPath() location
$tempFile = New-TemporaryFile
# Set the context to the subscription you want to use
# If your functionApp has access to more than one subscription it will load the first subscription by default.
# Possibly a good habit to be explicit about context.
Set-AzContext -Subscription $subscriptionId
# Get the Storage Account Key to authenticate
$storAccKeys = Get-AzStorageAccountKey -ResourceGroupName $resourceGroupName -Name $storageAccountName
$primaryKey = $storAccKeys | Where-Object keyname -eq 'key1' | Select-Object -ExpandProperty value
# Write the CIDR list to the temp file created earlier
$cidrList | Out-File $tempFile
# Create a Storage Context which will be used in the subsequent commands
$storageContext = New-AzStorageContext -StorageAccountName $storageAccountName -StorageAccountKey $primaryKey
# Upload the temp file to blob storage
$setAzStorageBlobContentSplat = #{
Container = $containerName
File = $tempFile.FullName
Blob = 'ipranges.txt'
Context = $storageContext
Properties = #{
ContentType = 'text/plain'
}
}
Set-AzStorageBlobContent #setAzStorageBlobContentSplat
# Return a simple response so I know it worked
Push-OutputBinding -Name Response -Value (
[HttpResponseContext]#{
StatusCode = [HttpStatusCode]::OK
Body = 'Successfully Updated Blob Storage'
}
)
You can see the documentation on Set-AzStorageBlobContent for examples on that here:
https://learn.microsoft.com/en-us/powershell/module/az.storage/set-azstorageblobcontent?view=azps-6.2.1#examples

Add tag to an AWS EC2 instance using AWS PowerShell?

How do I use the Amazon Web Services (AWS) PowerShell module to add a tag (key-value pair) to an EC2 instance?
First, install the AWS PowerShell module on PowerShell 5.0 or later:
Install-Module -Name AWSPowerShell -Scope CurrentUser -Force
Now, go to the IAM console and generate an access key, and use the following command to set your credentials in PowerShell.
$AccessKey = '<YourAccessKey>'
$SecretKey = '<SecretKey>'
$Region = 'us-west-1'
Initialize-AWSDefaults -AccessKey $AccessKey -SecretKey $SecretKey -Region $Region
Now that you're authenticated from PowerShell, use the following one-liner script to pop open a list of EC2 instances in a WPF window, and select the ones you want to tag.
(Get-EC2Instance).Instances |
Out-GridView -OutputMode Multiple |
ForEach-Object -Process {
New-EC2Tag -Tag (New-Object -TypeName Amazon.EC2.Model.Tag -ArgumentList #('Key', 'Value')) -Resource $PSItem.InstanceId
}
PS command
New-EC2Tag -ResourceId $Instances -Tags $Tags
From AWS documentation....
$tag = New-Object Amazon.EC2.Model.Tag
$tag.Key = "myTag"
$tag.Value = "myTagValue"
New-EC2Tag -Resource i-12345678 -Tag $tag
Need to add more than one tag!
$Tags = #()
$1T=New-Object Amazon.EC2.Model.Tag;$1T.key="KEYNAME";$1T.Value="VALUE";$Tags += $1T
$2T=New-Object Amazon.EC2.Model.Tag;$2T.key="2ndKEYNAME";$2T.Value="2ndVALUE";$Tags += $2T
$3T=New-Object Amazon.EC2.Model.Tag;$3T.key="3rdKEYNAME";$3T.Value="3rdVALUE";$Tags += $3T
$4T=New-Object Amazon.EC2.Model.Tag;$4T.key="4thKEYNAME";$4T.Value="4thVALUE";$Tags += $4T
$5T=New-Object Amazon.EC2.Model.Tag;$5T.key="5thKEYNAME";$5T.Value="5thVALUE";$Tags += $5T
New-EC2Tag -Resource i-12345678 -Tag $tag

A positional parameter cannot be found that accepts argument when running New-AzureQuickVM

I've the following PowerShell script:
Param(
$AccountName = "AccountName",
$Guid = "Get-Random",
$ImageName = "ImageName",
$vmName = "VM-$Guid",
$ServiceName = "ServiceName",
$adminLogin = "adminLogin",
$adminPasswd = "adminPasswd!",
$location = "South Central US",
$instanceSize = "Medium",
$subscriptionDataFile = "file.publishsettings"
)
########################Import Azure Public Setting And Assign Storage###########################
Import-AzurePublishSettingsFile $subscriptionDataFile
Set-AzureSubscription -SubscriptionName $AccountName -CurrentStorageAccountName scrprod -PassThru
#########################Create A VM From Existing Image And Remove It##########################
for($i=1; $i -le 5; $i++)
{
New-AzureQuickVM –Windows –Location $location –ServiceName $ServiceName `
–Name $vmName –InstanceSize $InstanceSize –ImageName $ImageName–AdminUsername $adminLogin –Password $adminPasswd -WaitForBoot
})
and I get the following exception:
New-AzureQuickVM : A positional parameter cannot be found that accepts argument 'â€Windows â€Location'.
What am I doing wrong?
Second I'd like to create 5 VM's with a unique name - Is that the correct way to do it?
Thanks,
Oren
I believe the encoding of your text file is incorrect, or you are using the incorrect characters (perhaps from a cut and paste).
Your code
New-AzureQuickVM –Windows –Location
Is somehow being evaluated as
New-AzureQuickVM 'â€Windows â€Location'
I suggest deleting the - characters before Windows and Location (and elsewhere likely) and then re-typing it. Also check your file's encoding and character set.
You have formatting errors in the call of the New-AzureQuickVM cmdlet, most notably the backtick after $ServiceName could cause issues if not immediately followed by a new line. Also, there should be a space between $ImageName and -AdminUsername.
I terms of the guid, I would create one using $guid = ([guid]::NewGuid()).ToString() but really the definition of random is up to you.