Cloudformation template in YAML is failing to parse the user data powershell script - amazon-web-services

I have written a cloudformation template in YAML, and everything runs smoothly but now instead of manually going into powershell to add a local group member and install some windows features I want to add the powershell commands to the user data portion of the AWS::EC2::Instance properties.
Here's the template in short:
Resources:
Instance:
Properties:
UserData:
Fn::Base64: |
<powershell>
add-localgroupmember (my group member)
install-windowsfeature (my windows feature)
</powershell>
weirdly enough the local group member gets added automatically but the windows feature doesn't get installed. Is there a certain format for the commands when they are multi line?
Here is the log error:
2022-11-25 19:48:58 Info: Try parsing user data in yaml format
2022-11-25 19:48:58 Info: Parsing failed, fall back to XML format
2022-11-25 19:48:58 Info: Converting user data to yaml format
I have tried to format the powershell script differently, nothing changed. I also tried adding the script one command at a time and so far the only command that works is the local group member and not the windows feature installing.
I took this same template and added an outfile only to check where the powershell script stops. Sometimes these files are created and sometimes they are not. Same with the adding local group member line. Only sometimes they are added. I am not sure what is going on here.

the below yml template adds a user as well as adds a feature.
Let me know if it is helpful.
Resources:
WebServer1:
Type: AWS::EC2::Instance
Properties:
ImageId: ami-0be29bafdaad782db
InstanceType: t2.micro
KeyName: NVirg-KP
SecurityGroups:
- EC2-AllTraffic-SG
UserData:
Fn::Base64: |
<powershell>
#username and password
$username = "ec2-user1"
$description = "ec2-user1"
$password = ConvertTo-SecureString "Login#1234" -AsPlainText -Force
#creating the user
New-LocalUser -Name $username -Password $password -FullName $username -Description $description
Add-LocalGroupMember -Group Administrators -Member $username
#Installing a feature
Import-Module ServerManager
Install-WindowsFeature -Name Web-Server
</powershell>

Related

Rename ec2 hostname using userdata does not work

I am trying to rename the hostname to a specific one - TEAM-CNTNR using the user-data script but after the EC2 instance comes up online and I connect to it (via Session Manager), the hostname is the random one that EC2 service gives the instance such as EC2AMAZ-VHAGRNV.
This is my user-data script, am I missing something? This is my user-data script:
<powershell>
Import-Module ECSTools
[Environment]::SetEnvironmentVariable("ECS_ENABLE_AWSLOGS_EXECUTIONROLE_OVERRIDE", $TRUE, "Machine")
Initialize-ECSAgent -Cluster "${cluster_name}" -EnableTaskIAMRole -LoggingDrivers '["json-file","awslogs"]' -EnableTaskENI
# rename the instance hostname so that it works with the gMSA account
Rename-Computer -NewName "TEAM-CNTNR" -Force
## instance-domain-join code here. Omitted for brevity
# Perform the domain join
Add-Computer -DomainName "$domain_name.$domain_tld" -OUPath "OU=Computers,OU=enrcloud,DC=enr,DC=cloud" -ComputerName "$hostname" -Credential $credential -Passthru -Verbose -Restart
</powershell>
<runAsLocalSystem>true</runAsLocalSystem>
It was revealed to me that there is a NewName parameter that can be used that is part of the Add-Computer command that would:
join the machine to the new domain
and change its name at the same time
Add-Computer ... -NewName "MyComputer" ...
Reference: Microsoft site

Scheduling a Powershell script to run weekly in AWS

So, I've got the following powershell script to find inactive AD users and disable their accounts, creating a log file containing a list of what accounts have been disabled:
Import-Module ActiveDirectory
# Set the number of days since last logon
$DaysInactive = 60
$InactiveDate = (Get-Date).Adddays(-($DaysInactive))
# Get AD Users that haven't logged on in xx days
$Users = Get-ADUser -Filter { LastLogonDate -lt $InactiveDate -and Enabled -eq $true } -
Properties LastLogonDate | Select-Object #{ Name="Username"; Expression=.
{$_.SamAccountName} }, Name, LastLogonDate, DistinguishedName
# Export results to CSV
$Users | Export-Csv C:\Temp\InactiveUsers.csv -NoTypeInformation
# Disable Inactive Users
ForEach ($Item in $Users){
$DistName = $Item.DistinguishedName
Disable-ADAccount -Identity $DistName
Get-ADUser -Filter { DistinguishedName -eq $DistName } | Select-Object #{ Name="Username"; Expression={$_.SamAccountName} }, Name, Enabled
}
The script works and is doing everything it should. What I am trying to figure out is how to automate this in an AWS environment.
I'm guessing I need to use a Lambda function in AWS to trigger this script to run on a schedule but don't know where to start.
Any help greatly appreciated.
I recomment to create a Lambda function with dotnet environment: https://docs.aws.amazon.com/lambda/latest/dg/lambda-powershell.html
Use a CloudWatch Event on a Scheduled basis to trigger the function:
https://docs.aws.amazon.com/AmazonCloudWatch/latest/events/RunLambdaSchedule.html
An alternative, if you like to to have a more pipeline style execution you could use CodePipeline and CodeBuild to run the script. Use again CloudWatch to trigger the CodePipeline on a scheduled basis!

Environment Variables in newest AWS EC2 instance

I am trying to get ENVIRONMENT Variables into the EC2 instance (trying to run a django app on Amazon Linux AMI 2018.03.0 (HVM), SSD Volume Type ami-0ff8a91507f77f867 ). How do you get them in the newest version of amazon's linux, or get the logging so it can be traced.
user-data text (modified from here):
#!/bin/bash
#trying to get a file made
touch /tmp/testfile.txt
cat 'This and that' > /tmp/testfile.txt
#trying to log
echo 'Woot!' > /home/ec2-user/user-script-output.txt
#Trying to get the output logged to see what is going wrong
exec > >(tee /var/log/user-data.log|logger -t user-data ) 2>&1
#trying to log
echo "XXXXXXXXXX STARTING USER DATA SCRIPT XXXXXXXXXXXXXX"
#trying to store the ENVIRONMENT VARIABLES
PARAMETER_PATH='/'
REGION='us-east-1'
# Functions
AWS="/usr/local/bin/aws"
get_parameter_store_tags() {
echo $($AWS ssm get-parameters-by-path --with-decryption --path ${PARAMETER_PATH} --region ${REGION})
}
params_to_env () {
params=$1
# If .Ta1gs does not exist we assume ssm Parameteres object.
SELECTOR="Name"
for key in $(echo $params | /usr/bin/jq -r ".[][].${SELECTOR}"); do
value=$(echo $params | /usr/bin/jq -r ".[][] | select(.${SELECTOR}==\"$key\") | .Value")
key=$(echo "${key##*/}" | /usr/bin/tr ':' '_' | /usr/bin/tr '-' '_' | /usr/bin/tr '[:lower:]' '[:upper:]')
export $key="$value"
echo "$key=$value"
done
}
# Get TAGS
if [ -z "$PARAMETER_PATH" ]
then
echo "Please provide a parameter store path. -p option"
exit 1
fi
TAGS=$(get_parameter_store_tags ${PARAMETER_PATH} ${REGION})
echo "Tags fetched via ssm from ${PARAMETER_PATH} ${REGION}"
echo "Adding new variables..."
params_to_env "$TAGS"
Notes -
What i think i know but am unsure
the user-data script is only loaded when it is created, not when I stop and then start mentioned here (although it also says [i think outdated] that the output is logged to /var/log/cloud-init-output.log )
I may not be starting the instance correctly
I don't know where to store the bash script so that it can be executed
What I have verified
the user-data text is on the instance by ssh-ing in and curl http://169.254.169.254/latest/user-data shows the current text (#!/bin/bash …)
What Ive tried
editing rc.local directly to export AWS_ACCESS_KEY_ID='JEFEJEFEJEFEJEFE' … and the like
putting them in the AWS Parameter Store (and can see them via the correct call, I just can't trace getting them into the EC2 instance without logs or confirming if the user-data is getting run)
putting ENV variables in Tags and importing them as mentioned here:
tried outputting the logs to other files as suggested here (Not seeing any log files in the ssh instance or on the system log)
viewing the System Log on the aws webpage to see any errors/logs via selecting the instance -> 'Actions' -> 'Instance Settings' -> 'Get System Log' (not seeing any commands run or log statements [only 1 unrelated word of user])

How to collect a grep and use it in a aws configset

In my aws Cloud Formation cfn configset I have a command to set an environment key to the name of the user group apache belongs to as it might be apache or www-data depending on the distro.
Something like this:
Metadata:
AWS::CloudFormation::Init:
configSets:
joomla:
- "set_permissions"
- "and_some_more..."
configure_cfn:
files:
/etc/cfn/hooks.d/cfn-auto-reloader.conf:
content: !Sub |
[cfn-auto-reloader-hook]
triggers=post.update
path=Resources.EC2.Metadata.AWS::CloudFormation::Init
action=/opt/aws/bin/cfn-init -v --stack ${AWS::StackName} --resource EC2 --configsets joomla --region ${AWS::Region}
mode: "000400"
owner: root
group: root
.....
set_permissions:
commands:
01_01_get_WebServerGroup:
env:
#webserver group might be apache or www-data depending on the distro
WebServerGp:
command: "ps -ef | egrep '(httpd|apache2|apache)' | grep -v `whoami` | grep -v root | head -n1 | awk '{print $1}'"
However, when I launch this stack, the configsets process halts at this point and I get an an error in the cfn_init.log that looks like this:
File
"/usr/lib/python2.7/dist-packages/cfnbootstrap/command_tool.py", line
80, in apply
raise ToolError(u"%s does not specify the 'command' attribute, which is required" % name) ToolError: 01_01_get_WebServerGroup does
not specify the 'command' attribute, which is required
Is this the preferred method to catch and use a grep result in a configset command? Is there a better way? What can I do to address the error thrown in the cfn_init.log?
OK, I guess I can create parameter and mapping elements to capture the distro type on launch and then set the webserver group accordingly but I am really trying to understand how to set the env: key to a response from the cli.
The problem of your code is this line WebServerGp.
Line command is must be on the same level of env, under the commands name, in your case is 01_01_get_WebServerGroup. So, it has to be like this:
commands:
01_01_get_WebServerGroup:
env: ..
command: ..
If you want to use the result of grep, you can put them on variable and use it later.
You can specify more than one command under that command line using \n for executing the command.
Please check this code below.
command: "result=(ps ef | grep ...)\n echo $result\n ..."
If you have really long command, you can use the Fn::Join to be the value of command.

Unable to execute bash scripts from heat template

I am new to heat templates and I am trying to run bash scripts from the heat template. However, the instance is coming up in active state but the shell script was not executed at all. Any suggestions will be highly appreciated.
Hope this is helpful for you.
parameters: DBRootPassword:
type: string
label: Database Password
description: Root password for MySQL
hidden: true
resources: my_instance:
type: OS::Nova::Server
properties:
# general properties ...
user_data:
str_replace:
template: |
#!/bin/bash
echo "Hello world"
echo "Setting MySQL root password"
mysqladmin -u root password $db_rootpassword
# do more things ...
params:
$db_rootpassword: { get_param: DBRootPassword }