How to update a security group using lambda function? - python

I'm trying to update AWS security group with one inbound rules using lambda function Python 3.7. For example: I would like to add my public IP with 8443 Port in existing security group. I have followed below link to achieve.
Modifying ec2 security group using lambda function
When I used this code on Lambda function with Python 3.7, it's not working.
import boto3
import hashlib
import json
import copy
import urllib2
# ID of the security group we want to update
SECURITY_GROUP_ID = "sg-XXXX"
# Description of the security rule we want to replace
SECURITY_RULE_DESCR = "My Home IP"
def lambda_handler(event, context):
new_ip_address = list(event.values())[0]
result = update_security_group(new_ip_address)
return result
def update_security_group(new_ip_address):
client = boto3.client('ec2')
response = client.describe_security_groups(GroupIds=[SECURITY_GROUP_ID])
group = response['SecurityGroups'][0]
for permission in group['IpPermissions']:
new_permission = copy.deepcopy(permission)
ip_ranges = new_permission['IpRanges']
for ip_range in ip_ranges:
if ip_range['Description'] == 'My Home IP':
ip_range['CidrIp'] = "%s/32" % new_ip_address
client.revoke_security_group_ingress(GroupId=group['GroupId'], IpPermissions=
[permission])
client.authorize_security_group_ingress(GroupId=group['GroupId'], IpPermissions=
[new_permission])
return ""
I got response as "", when I tested this code on lambda function.

The revoke_security_group_ingress() and authorize_security_group_ingress() calls in boto3 includes a Return field in the response:
Returns true if the request succeeds; otherwise, returns an error.
However, your code does not seem to be storing a response or examining its contents.

Related

Update Org Policy constraints with Python

I am working on a small project, to update an org policy constraints by using python.
I want to use python because I have set up Secret Manager and Impersonation.
Right now I am at this final stage, of modifying the org policy constraint
I have found the repo https://github.com/googleapis/python-org-policy/tree/40faa07298b3baa9a4d0ca26927b28fdd80aa03b/samples/generated_samples
With a code sample for creating a constraint.
I would like to modify this: "projects/project-id-from-gcp/policies/compute.skipDefaultNetworkCreation" to Enforced.
The code I have so far, is this:
from google.cloud import orgpolicy_v2
def sample_update_policy():
# Create a client
client = orgpolicy_v2.OrgPolicyClient()
# Initialize request argument(s)
request = orgpolicy_v2.UpdatePolicyRequest(
policy="""
name: "projects/project-id-from-gcp/policies/compute.skipDefaultNetworkCreation"
spec {
rules {
enforce: true
}
}
"""
)
# Make the request
response = client.update_policy(request=request)
#
# Handle the response
print(response)
sample_update_policy()
But I get the error google.api_core.exceptions.InvalidArgument: 400 Request contains an invalid argument.
I do not understand what to write exactly in "CreatePolicyRequest".
I also found this, https://googleapis.dev/python/orgpolicy/1.0.2/orgpolicy_v2/types.html#google.cloud.orgpolicy_v2.types.Policy but it is not exactly clear to me.
I was looking at this https://cloud.google.com/python/docs/reference/orgpolicy/latest/google.cloud.orgpolicy_v2.services.org_policy.OrgPolicyClient#google_cloud_orgpolicy_v2_services_org_policy_OrgPolicyClient_update_policy
But i honestly do not understand how to do it.
(I do not think what I modified it is even correct. )
Could you, please, point me in the right direction?
Thank you
Your problem is that you are passing a YAML string as the parameter to UpdatePolicyRequest(). You were on the correct path with your links.
from google.cloud import orgpolicy_v2
from google.cloud.orgpolicy_v2 import types
def build_policy():
rule = types.PolicySpec.PolicyRule()
rule.enforce = True
spec = types.PolicySpec()
spec.rules.append(rule)
policy = types.Policy(
name="projects/project-id-from-gcp/policies/compute.skipDefaultNetworkCreation",
spec = spec
)
return policy
def sample_update_policy():
# Create a client
client = orgpolicy_v2.OrgPolicyClient()
policy = build_policy()
# Debug - view created policy
print(policy)
# Initialize request argument(s)
request = orgpolicy_v2.UpdatePolicyRequest(
policy=policy
)
# Make the request
response = client.update_policy(request=request)
#
# Handle the response
print(response)
sample_update_policy()

Python Lambda giving botocore.errorfactory.InvalidLambdaResponseException when triggered on postconfirmation

I have setup AWS Lambda function that is triggered on an AWS Cognito. The trigger on a successful email confirmation. The Lambda function is in Python3.6.
I am referring to the AWS documentation for Cognito postConfirmation trigger.
https://docs.aws.amazon.com/cognito/latest/developerguide/user-pool-lambda-post-confirmation.html
"response": {}
So far I have tried returning None, {}, '{}'(empty json string) or valid dictionary like {'status':200, 'message':'the message string'} but it is giving error.
botocore.errorfactory.InvalidLambdaResponseException: An error occurred (InvalidLambdaResponseException) when calling the ConfirmSignUp operation: Unrecognizable lambda output
What should be a valid response for the post confirmation function?
here is the part of code.
from DBConnect import user
import json
def lambda_handler(event, context):
ua = event['request']['userAttributes']
print("create user ua = ", ua)
if ('name' in ua):
name = ua['name']
else:
name = "guest"
newUser = user.create(
name = name,
uid = ua['sub'],
owner = ua['sub'],
phoneNumber = ua['phone_number'],
email = ua['email']
)
print(newUser)
return '{}' # <--- I am using literals here only.
You need to return the event object:
return event
This is not obvious in the examples they provide in the documentation. You may want to check and ensure the event object does contain a response key (it should).

Python Lambda function access cognito "sub" uuid

I have a Lambda function written in python that is triggered by the API Gateway with proxy integration and a Cognito user pool authorizer. I am trying to access the "sub" UUID that Cognito gives every user, but nothing I try works. I have just about exhausted google of all related search results and nothing that I have found seems valid. They either return a null or they crash. My function (with every attempted UUID access line commented out) is below:
import json
def lambda_handler(event, context):
#UniqueUser = context.identity.cognito_identity_id
#UniqueUser = context.authorizer.claims.sub
#UniqueUser = event.requestContext.authorizer.claims.sub
#UniqueUser = event.request.userAttributes.sub
#UniqueUser = event.queryStringParameters.sub
try:
# Something that gets sub from cognito
except:
UniqueUser = "not valid code"
if not UniqueUser:
UniqueUser = "UniqueUser is null"
return {
'statusCode': 200,
'headers': {
'Access-Control-Allow-Origin': '*'
},
'body': json.dumps('Hello from Lambda ' + UniqueUser + "!")
};
Does anyone know any possible sources of this behavior or solution to this problem?
If you need to access the UUID of a Cognito user who is calling a Lambda function through the API Gateway with proxy integration, use the code below.
UniqueUser = event['requestContext']['authorizer']['claims']['sub']

python aws put_bucket_policy() MalformedPolicy

How does one use put_bucket_policy()? It throws a MalformedPolicy error even when I try to pass in an existing valid policy:
import boto3
client = boto3.client('s3')
dict_policy = client.get_bucket_policy(Bucket = 'my_bucket')
str_policy = str(dict_policy)
response = client.put_bucket_policy(Bucket = 'my_bucket', Policy = str_policy)
* error message: *
botocore.exceptions.ClientError: An error occurred (MalformedPolicy) when calling the PutBucketPolicy operation: This policy contains invalid Json
That's because applying str to a dict doesn't turn it into a valid json, use json.dumps instead:
import boto3
import json
client = boto3.client('s3')
dict_policy = client.get_bucket_policy(Bucket = 'my_bucket')
str_policy = json.dumps(dict_policy)
response = client.put_bucket_policy(Bucket = 'my_bucket', Policy = str_policy)
Current boto3 API doesn't have a function to APPEND the bucket policy, whether add another items/elements/attributes. You need load and manipulate the JSON yourself. E.g. write script load the policy into a dict, append the "Statement" element list, then use the policy.put to replace the whole policy. Without the original statement id, user policy will be appended. HOWEVER, there is no way to tell whether later user policy will override rules of the earlier one.
For example
import boto3
s3_conn = boto3.resource('s3')
bucket_policy = s3_conn.BucketPolicy('bucket_name')
policy = s3_conn.get_bucket_policy(Bucket='bucket_name')
user_policy = { "Effect": "Allow",... }
new_policy = policy['Statement'].append(user_policy)
bucket_policy.put(Policy=new_policy)
The user don't need know the old policy in the process.

Iterate thru ec2 describe instance boto3

I am trying to get specific values for a describe instance call. So for example, if I want to get the 'Hypervisor' value or the Ebs has 'DeleteOnTermintation' value from the output. Below is the current code I am currently using to make the call and iterate thru the dictionary output.
import boto3
import pprint
from datetime import datetime
import json
client = boto3.client('ec2')
filters = [{
'Name': 'tag:Name',
'Values': ['*']
}]
class DatetimeEncoder(json.JSONEncoder):
def default(self, obj):
if isinstance(obj, datetime):
return obj.strftime('%Y-%m-%dT%H:%M:%SZ')
elif isinstance(obj, date):
return obj.strftime('%Y-%m-%d')
# Let the base class default method raise the TypeError
return json.JSONEncoder.default(self, obj)
output = json.dumps((client.describe_instances(Filters=filters)), cls=DatetimeEncoder)
pprint.pprint(output)
for v in output:
print v['Hypervisor']
Getting this error:
TypeError: string indices must be integers, not str
Using the pprint to see all the values available from the output.
Here's how you could display the information via the AWS Command-Line Interface (CLI):
aws ec2 describe-instances --query 'Reservations[*].Instances[*].[InstanceId, Hypervisor, NetworkInterfaces[0].Attachment.DeleteOnTermination]'
Here's some Python:
import boto3
client = boto3.client('ec2')
response = client.describe_instances()
for r in response['Reservations']:
for i in r['Instances']:
print i['InstanceId'], i['Hypervisor']
for b in i['BlockDeviceMappings']:
print b['Ebs']['DeleteOnTermination']
Here's John's answer but updated for Python3
import boto3
client = boto3.client('ec2')
response = client.describe_instances()
for r in response['Reservations']:
for i in r['Instances']:
print(i['InstanceId'], i['Hypervisor'])
for b in i['BlockDeviceMappings']:
print(b['Ebs']['DeleteOnTermination'])
I know I am kinda late to the party, but my 2 cents for readability is to use generator comprehension (python 3):
import boto3
client = boto3.client('ec2')
response = client.describe_instances()
block_mappings = (block_mapping
for reservation in response["Reservations"]
for instance in reservation["Instances"]
for block_mapping in instance["BlockDeviceMappings"])
for block_mapping in block_mappings:
print(block_mapping["Ebs"]["DeleteOnTermination"])
You can also use jmespath, the same query engine behind awscli --query flag, to get the nested results automatically:
import jmespath
import boto3
client = boto3.client('ec2')
response = client.describe_instances()
print(jmespath.search(
"Reservations[].Instances[].DeviceBlockMappings[].Ebs.DeleteOnTermination",
response
))
Or, in case you need more power, use pyjq. Its syntax is a little different from jmespath which is used in awscli, but it has more advantages over it. Let's say you want not only the DeviceBlockMappings but also to keep to which InstanceId it is related to. In jmespath you can't really do this, because there is no access to outer structures, just a single nested path. In pyjq you can do something like this:
import pyjq
import boto3
client = boto3.client('ec2')
response = client.describe_instances()
print(pyjq.all(
"{id: .Reservations[].Instances[].InstanceId, d:.Reservations[].Instances[].DeviceBlockMappings[]}",
response
))
This will yield a list of device block mappings with their corresponding InstanceId, kind of like a mongo's unwind operation:
{'id': string, d: {'Ebs': {'DeleteOnTermination': boolean}}}[]
print (jmespath.search("Reservations[].Instances[].[InstanceId, SubnetId, ImageId, PrivateIpAddress, Tags[*]]", response))

Categories