How to call something inputted by user in the code? aws api - amazon-web-services

data = cd.get_cost_and_usage(TimePeriod={'Start':input("Enter Start Date in format yyyy-mm-dd:\n"),'End': input("Enter End Date in format yyyy-mm-dd:\n")}, Granularity='MONTHLY',
Metrics=[input('Choose any of the following metrics: AMORTIZED_COST, UNBLENDED_COST, BLENDED_COST, USAGE_QUANTITY, NET_AMORTIZED_COST\n')],
GroupBy=[{'Type': 'DIMENSION', 'Key': 'LINKED_ACCOUNT'}, {'Type': 'DIMENSION', 'Key':input("Enter any of the following Dimensions - SERVICE, INSTANCE_TYPE, USAGE_TYPE, RECORD_TYPE:\n")}], **kwargs)
for info in data['ResultsByTime']:
for group in info['Groups']:
data=(group['Keys'][0], info['TimePeriod']['Start'], group['Metrics'][]
['Amount'], group['Keys'][1])
print(*data, sep=", ", file=f, flush=True)
in the below part of the script, the [] needs to have the same input as Metrics, how do I make it so the User doesn't have to input the same thing twice? whatever they put as Metrics comes up in this as well?
data=(group['Keys'][0], info['TimePeriod']['Start'], group['Metrics'][]
['Amount'], group['Keys'][1])

I think the most logical way would be to extract the metric input along with the other inputs, and then re-use it.
For example:
metric_chosen = input('Choose any of the following metrics: AMORTIZED_COST, UNBLENDED_COST, BLENDED_COST, USAGE_QUANTITY, NET_AMORTIZED_COST\n')
date_start = input("Enter Start Date in format yyyy-mm-dd:\n")
date_end = input("Enter End Date in format yyyy-mm-dd:\n")
dimension_chosen = input("Enter any of the following Dimensions - SERVICE, INSTANCE_TYPE, USAGE_TYPE, RECORD_TYPE:\n")
data = cd.get_cost_and_usage(
TimePeriod={
'Start': date_start,
'End': date_end
},
Granularity='MONTHLY',
Metrics=[metric_chosen],
GroupBy=[
{'Type': 'DIMENSION',
'Key': 'LINKED_ACCOUNT'
},
{'Type': 'DIMENSION',
'Key': dimension_chosen
}], **kwargs)
for info in data['ResultsByTime']:
for group in info['Groups']:
data=(group['Keys'][0],
info['TimePeriod']['Start'],
group['Metrics'][metric_chosen]['Amount'],
group['Keys'][1])
print(*data, sep=", ", file=f, flush=True)

Related

Find the cost of each resource in AWS

I have a python script to get the cost of each service for the last month, but I want the cost for each resource using the tag mentioned for that resource. For example, I have got the cost for RDS service, am using two database instances, So I want to get the separate cost for two database instances . I have different tags for two database instances
TAGS:
First database instance --> Key: Name Value:rds1
Second database instance --> Key: Name Value:rds2
My output should be like , the tag of the resource and its cost
Example --> rds1 - 15$
rds2 - 10$
Can anyone help me to achieve this ?
I have attached the ouput I got for cost based on the service
Output for cost based on service
Similar work you can find here Boto3 get_cost_and_usage filter Getting zero cost based on tag
Make sure that the tags that you are listing are correct.
On top of this,
you could see all of the tags that have been created in your account by the user or it is AWS managed. here https://us-east-1.console.aws.amazon.com/billing/home#/tags.
with boto3 cost-explorer client, you could use this function list_cost_allocation_tags to get a list of cost allocation tags.
import boto3
start_date = '2022-07-01'
end_date = '2022-08-30'
client = boto3.client('ce')
tags_response = None
try:
tags_response = client.list_cost_allocation_tags(
Status='Inactive', # 'Active'|'Inactive',
# TagKeys=[
# 'Key',
# ],
Type='UserDefined', # 'AWSGenerated'|'UserDefined',
# NextToken='string',
# MaxResults=100,
)
except Exception as e:
print(e)
cost_allocation_tags = tags_response['CostAllocationTags']
print(cost_allocation_tags)
print("-"*5+' Input TagValues with comma separation '+"-"*5)
for cost_allocation_tag in cost_allocation_tags:
tag_key = cost_allocation_tag['TagKey']
tag_type = cost_allocation_tag['Type']
tag_status = cost_allocation_tag['Status']
tag_values = str(input(
f'TagKey: {tag_key}, Type: {tag_type}, Status: {tag_status} -> '))
if tag_values == "":
continue
tag_values_parsed = tag_values.strip().split(',')
if tag_values_parsed == []:
continue
cost_usage_response = None
try:
cost_usage_response = client.get_cost_and_usage(
TimePeriod={
'Start': start_date,
'End': end_date
},
Metrics=['AmortizedCost'],
Granularity='MONTHLY', # 'DAILY'|'MONTHLY'|'HOURLY',
Filter={
'Tags': {
'Key': tag_key,
'Values': tag_values_parsed,
'MatchOptions': [
'EQUALS' # 'EQUALS'|'ABSENT'|'STARTS_WITH'|'ENDS_WITH'|'CONTAINS'|'CASE_SENSITIVE'|'CASE_INSENSITIVE',
]
},
},
# GroupBy=[
# {
# 'Type': 'SERVICE', # 'DIMENSION'|'TAG'|'COST_CATEGORY', # AZ, INSTANCE_TYPE, LEGAL_ENTITY_NAME, INVOICING_ENTITY, LINKED_ACCOUNT, OPERATION, PLATFORM, PURCHASE_TYPE, SERVICE, TENANCY, RECORD_TYPE , and USAGE_TYPE
# 'Key': 'string',
# },
# ],
)
print(cost_usage_response)
except Exception as e:
print(e)

How do get the Linked_Account_Name while calling the cost explorer API

I have the below code to get the cost explorer details using boto3 which will give the data on the basis of account_id.I need the details on the basis of Linked_account_Name. Can someone guide me how to proceed..
response = ce.get_cost_and_usage(
TimePeriod={
'Start': '2020-01-01',
'End': '2020-01-03'
},
Granularity='MONTHLY',
Metrics=[
'UnblendedCost',
],
GroupBy=[
{
'Type': 'DIMENSION',
'Key': 'LINKED_ACCOUNT'
},
]
LINKED_ACCOUNT_NAME is not valid for all three context(COST_AND_USAGE','RESERVATIONS','SAVINGS_PLANS).
Dimensions are also limited to LINKED_ACCOUNT , REGION , or RIGHTSIZING_TYPE in get_cost_and_usage()
So, you won't be able to use it.
You can use
get_dimension_values()
use this link for more info
function to get the Linked Account name.
client = session.client('ce')
response = client.get_dimension_values(
SearchString='123456789098',
TimePeriod={
'Start': '2020-01-01',
'End': '2020-03-01'
},
Dimension='LINKED_ACCOUNT',
Context='COST_AND_USAGE'
)
for each in response['DimensionValues']:
print('Account Name is ->', each['Attributes']['description'])
and output will be like below:
Account Name is -> Test 0100
Its not a complete answer but you can proceed from here.

How to send email to multiple recipients through AWS SES

Hello guys i'm trying to send email to multiple user through AWS SES using Python but whenever i'm trying to send a mail i got a error : Illegal address
This is my code:
def emailServiceForCustomerInformation(self, emailSubject, customerLicenseMessage, installation_name):
# logger = ToolsLogger.getOrCreateLogger(current_user.keyspace)
logger = ToolsLogger.getOrCreateRootLogger()
logger.info("Email service For Customer is started")
record = int(recordCount)
# print("emailRcord-",record)
# This address must be verified with Amazon SES.
SENDER = "Snehil singh<snehil#codedata.io>"
# is still in the sandbox, this address must be verified.
recipients = ["cosmoandysysmo#gmail.com","snehil#codedata.io"]
RECIPIENT = ", ".join(recipients)
# If necessary, replace us-east-1 with the AWS Region currently using for Amazon SES.
AWS_REGION = "us-east-1"
# The subject line for the email.
SUBJECT = emailSubject
BODY_TEXT = (customerLicenseMessage + ' ''For InstallationName-'+ installation_name)
# The character encoding for the email.
CHARSET = "UTF-8"
client = boto3.client('ses', region_name=AWS_REGION,
aws_access_key_id=config[os.environ['CONFIG_TYPE']].S3_ACCESS_KEY,
aws_secret_access_key=config[os.environ['CONFIG_TYPE']].S3_ACCESS_SECRET_KEY,
config=Config(signature_version='s3v4'))
is_success = True
# Try to send the email.
try:
# Provide the contents of the email.
response = client.send_email(
Destination={
'ToAddresses': [
RECIPIENT,
],
},
Message={
'Body': {
'Text': {
'Charset': CHARSET,
'Data': BODY_TEXT,
},
},
'Subject': {
'Charset': CHARSET,
'Data': SUBJECT,
},
},
Source=SENDER,
# If you are not using a configuration set, comment or delete the
# following line
# ConfigurationSetName=CONFIGURATION_SET,
)
# Display an error if something goes wrong.
except ClientError as e:
logger.exception(e)
print(e.response['Error']['Message'])
is_success = False
else:
# print("Email sent! Message ID:"),
# print(response['MessageId'])
logger.info("Email service is Completed and send to the mail")
return is_success
i have searched on internet but non of the answer helped This is another way i have tried https://www.jeffgeerling.com/blogs/jeff-geerling/sending-emails-multiple but this also not helpful please help me where i'm doing wrong where do i modify it please ping me if you have any questions related this...thanks in advance.
Looks to me like you should be passing in the 'recipient', not the RECIPENT string. Try something like this:
Destination={'ToAddresses':recipients}
It appears to be expecting an array, not a comma seperated list of strings.
In boto3 SES send_email documentation:
response = client.send_email(
Source='string',
Destination={
'ToAddresses': [
'string',
],
'CcAddresses': [
'string',
],
'BccAddresses': [
'string',
]
},
And if you read the SES SendEmail API call documentation, it tells you that the Destination object is:
BccAddresses.member.N
The BCC: field(s) of the message.
Type: Array of strings
Required: No
CcAddresses.member.N
The CC: field(s) of the message.
Type: Array of strings
Required: No
ToAddresses.member.N
The To: field(s) of the message.
Type: Array of strings
Required: No
In summary: don't join the address to construct RECIPIENT. RECIPIENT needs to be an array (a list, in Python) of strings, where each strings is one email address.
RECIPIENT must be array of strings > ['email1', 'email2']
and >>
Destination={
'ToAddresses': [
RECIPIENT,
],
},
to
Destination={
'ToAddresses': RECIPIENT
},
Credit to this answer
All you need to do is to put all the recipients as a list
email = ['xxx#gmail.com', 'yyy#yahoo.com', 'zzz#hotmail.com]
Then, you can modify the boto3 variables as below
Destination={'ToAddresses': email, .....}

Aws cost and usage for all the instances

I would like to get the usage cost report of each instance in my aws account form a period of time.
I'm able to get linked_account_id and service in the output but I need instance_id as well. Please help
import argparse
import boto3
import datetime
cd = boto3.client('ce', 'ap-south-1')
results = []
token = None
while True:
if token:
kwargs = {'NextPageToken': token}
else:
kwargs = {}
data = cd.get_cost_and_usage(
TimePeriod={'Start': '2019-01-01', 'End': '2019-06-30'},
Granularity='MONTHLY',
Metrics=['BlendedCost','UnblendedCost'],
GroupBy=[
{'Type': 'DIMENSION', 'Key': 'LINKED_ACCOUNT'},
{'Type': 'DIMENSION', 'Key': 'SERVICE'}
], **kwargs)
results += data['ResultsByTime']
token = data.get('NextPageToken')
if not token:
break
print('\t'.join(['Start_date', 'End_date', 'LinkedAccount', 'Service', 'blended_cost','unblended_cost', 'Unit', 'Estimated']))
for result_by_time in results:
for group in result_by_time['Groups']:
blended_cost = group['Metrics']['BlendedCost']['Amount']
unblended_cost = group['Metrics']['UnblendedCost']['Amount']
unit = group['Metrics']['UnblendedCost']['Unit']
print(result_by_time['TimePeriod']['Start'], '\t',
result_by_time['TimePeriod']['End'],'\t',
'\t'.join(group['Keys']), '\t',
blended_cost,'\t',
unblended_cost, '\t',
unit, '\t',
result_by_time['Estimated'])
As far as I know, Cost Explorer can't treat the usage per instance. There is a function Cost and Usage Reports which gives a detailed billing report by dump files. In this file, you can see the instance id.
It can also be connected to the AWS Athena. Once you did this, then directly query to the file on Athena.
Here is my presto example.
select
lineitem_resourceid,
sum(lineitem_unblendedcost) as unblended_cost,
sum(lineitem_blendedcost) as blended_cost
from
<table>
where
lineitem_productcode = 'AmazonEC2' and
product_operation like 'RunInstances%'
group by
lineitem_resourceid
The result is
lineitem_resourceid unblended_cost blended_cost
i-***************** 279.424 279.424
i-***************** 139.948 139.948
i-******** 68.198 68.198
i-***************** 3.848 3.848
i-***************** 0.013 0.013
where the resourceid containes the instance id. The amount of cost is summed for all usage in this month. For other type of product_operation, it will contains different resource ids.
You can add an individual tag to all instances (e.g. Id) and then group by that tag:
GroupBy=[
{
'Type': 'TAG',
'Key': 'Id'
},
],

cannot make more than one spot request using boto3 in one day

Greeting,
My code work as long as its the first spot request of the day. If I terminate the instance and make another spot request It just gives me back my old request.
Is there something with my code or with AWS ??? Is there work-around ??
I have tried to clone my AMI and then use the clone AMI change the price or change the number of instance in the spec
but it is still not working ???
!/home/makayo/.virtualenvs/boto3/bin/python
"""
http://boto3.readthedocs.io/en/latest/reference/services/ec2.html#EC2.Client.describe_spot_instance_requests
"""
import boto3
import time
myid=
s = boto3.Session()
ec2 = s.resource('ec2')
client = boto3.client('ec2')
images = list(ec2.images.filter(Owners=[myid]))
def getdate(datestr):
ix=datestr.replace('T',' ')
ix=ix[0:len(ix)-5]
idx=time.strptime(ix,'%Y-%m-%d %H:%M:%S')
return(idx)
zz=sorted(images, key=lambda images: getdate(images.creation_date))
#last_ami
myAmi=zz[len(zz)-1]
#earliest
#myAmi=latestAmi=zz[0]
"""
[{u'DeviceName': '/dev/sda1', u'Ebs': {u'DeleteOnTermination': True, u'Encrypted': False, u'SnapshotId': 'snap-d8de3adb', u'VolumeSize': 50, u'VolumeType': 'gp2'}}]
"""
#myimageId='ami-42870a55'
myimageId=myAmi.id
print myimageId
mysubnetId= myinstanceType='c4.4xlarge'
mykeyName='spot-coursera'
#make sure ajust this but dont do multiple in a loop as it can fail!!!
mycount=2
#make sure ajust this but dont do multiple in a loop as it can fail!!!
myprice='5.0'
mytype='one-time'
myipAddr=
myallocId=''
mysecurityGroups=['']
#mydisksize=70
mygroupId=
#mygroupId=
myzone='us-east-1a'
myvpcId='vpc-503dba37'
#latestAmi.block_device_mappings[0]['Ebs']['VolumeSize']=mydisksize
#diskSpec=latestAmi.block_device_mappings[0]['Ebs']['VolumeSize']
response2 = client.request_spot_instances(
DryRun=False,
SpotPrice=myprice,
ClientToken='string',
InstanceCount=1,
Type='one-time',
LaunchSpecification={
'ImageId': myimageId,
'KeyName': mykeyName,
'SubnetId':mysubnetId,
#'SecurityGroups': mysecurityGroups,
'InstanceType': myinstanceType,
'Placement': {
'AvailabilityZone': myzone,
}
}
)
#print(response2)
myrequestId=response2['SpotInstanceRequests'][0]['SpotInstanceRequestId']
import time
XX=True
while XX:
response3 = client.describe_spot_instance_requests(
#DryRun=True,
SpotInstanceRequestIds=[
myrequestId,
]
#Filters=[
# {
# 'Name': 'string',
# 'Values': [
# 'string',
# ]
#},
#]
)
#print(response3)
request_status=response3['SpotInstanceRequests'][0]['Status']['Code']
if(request_status=='fullfilled'):
print myrequestId,request_status
XX=False;
elif ('pending' in request_status):
print myrequestId,request_status
time.sleep(5)
else:
XX=False
print myrequestId,request_status
"""
instances = ec2.instances.filter(Filters=[{'Name': 'instance-state-name', 'Values': ['running']}])
while( len(list(instances))==0):
instances = ec2.instances.filter(Filters=[{'Name': 'instance-state-name', 'Values': ['running']}])
instances = ec2.instances.filter(Filters=[{'Name': 'instance-state-name', 'Values': ['running']}])
for instance in instances:
print(instance.id, instance.instance_type);
response = instance.modify_attribute(Groups=[mygroupId]);
print(response);
This is wrong:
ClientToken='string',
Or, at least, it's wrong most of the time, as you should now realize.
The purpose of the token is to ensure that EC2 does not process the same request twice, due to retries, bugs, or a multitude of other reasons.
It doesn't matter (within reason -- 64 characters, max, ASCII, case-sensitive) what you send here, but you need to send something different with each unique request.
A client token is a unique, case-sensitive string of up to 64 ASCII characters. It is included in the response when you describe the instance. A client token is valid for at least 24 hours after the termination of the instance. You should not reuse a client token in another call later on.
http://docs.aws.amazon.com/AWSEC2/latest/APIReference/Run_Instance_Idempotency.html