1"use strict";(self.webpackChunkinfracost_docs=self.webpackChunkinfracost_docs||[]).push([[5207],{81884(e,s,a){a.r(s),a.d(s,{assets:()=>d,contentTitle:()=>u,default:()=>m,frontMatter:()=>c,metadata:()=>r,toc:()=>h});const r=JSON.parse('{"id":"features/usage_based_resources","title":"Usage costs","description":"Infracost differentiates Baseline costs and Usage costs:","source":"@site/docs/features/usage_based_resources.md","sourceDirName":"features","slug":"/features/usage_based_resources","permalink":"/docs/features/usage_based_resources","draft":false,"unlisted":false,"tags":[],"version":"current","frontMatter":{"slug":"usage_based_resources","title":"Usage costs"},"sidebar":"someSidebar","previous":{"title":"CLI commands","permalink":"/docs/features/cli_commands"},"next":{"title":"Config file","permalink":"/docs/features/config_file"}}');var n=a(74848),t=a(28453),o=a(4865),l=a(19365),i=a(86025);const c={slug:"usage_based_resources",title:"Usage costs"},u=void 0,d={},h=[{value:"How to override & improve estimates",id:"how-to-override--improve-estimates",level:2},{value:"Infracost Cloud",id:"infracost-cloud",level:2},{value:"Predefined values",id:"predefined-values",level:3},{value:"Add overrides",id:"add-overrides",level:3},{value:"infracost-usage.yml",id:"infracost-usageyml",level:2},{value:"Resource arrays/maps",id:"resource-arraysmaps",level:4},{value:"EC2 reserved instances",id:"ec2-reserved-instances",level:4}];function f(e){const s={a:"a",code:"code",h2:"h2",h3:"h3",h4:"h4",li:"li",ol:"ol",p:"p",pre:"pre",strong:"strong",ul:"ul",...(0,t.R)(),...e.components},{Details:a}=s;return a||function(e,s){throw new Error("Expected "+(s?"component":"object")+" `"+e+"` to be defined: you likely forgot to import, pass, or provide it.")}("Details",!0),(0,n.jsxs)(n.Fragment,{children:[(0,n.jsx)(s.p,{children:"Infracost differentiates Baseline costs and Usage costs:"}),"\n",(0,n.jsxs)(s.ul,{children:["\n",(0,n.jsxs)(s.li,{children:[(0,n.jsx)(s.strong,{children:"Baseline costs"})," are consistent charges for provisioned resources, like the hourly cost for a virtual machine, which stays constant no matter how much it is used. Infracost estimates these resources assuming they are used for the whole month (730 hours)."]}),"\n",(0,n.jsxs)(s.li,{children:[(0,n.jsx)(s.strong,{children:"Usage costs"})," are charges based on actual usage, like the storage cost for an object storage bucket. Infracost estimates these resources using monthly usage values defined in your ",(0,n.jsx)(s.a,{href:"#infracost-cloud",children:"Infracost Cloud"})," organization or from an ",(0,n.jsx)(s.a,{href:"#infracost-usageyml",children:"infracost-usage.yml"})," file at the root of code repos."]}),"\n"]}),"\n",(0,n.jsxs)(s.p,{children:["To determine whether a resource incurs baseline or usage costs, you can examine cloud vendor pricing details, paying attention to whether prices are listed hourly. If they are and your Terraform code specifies resource size, it indicates a baseline cost. Alternatively, you can utilize Infracost; costs marked with a ",(0,n.jsx)(s.code,{children:"*"})," denote usage costs, while others represent baseline costs. Infracost exists to make cloud pricing easy to understand!"]}),"\n",(0,n.jsx)("img",{src:(0,i.Ay)("img/infracost-cloud/pull-request-with-usage-cost.png"),alt:"Usage costs in pull requests"}),"\n",(0,n.jsx)(s.h2,{id:"how-to-override--improve-estimates",children:"How to override & improve estimates"}),"\n",(0,n.jsx)(s.p,{children:"Check out our demo video below to learn how to override and improve estimates for usage-based resources. This can be done from:"}),"\n",(0,n.jsxs)(s.ul,{children:["\n",(0,n.jsxs)(s.li,{children:[(0,n.jsx)(s.a,{href:"#infracost-cloud",children:"Infracost Cloud"})," (recommended): define usage defaults for all repos in a central place. This is a paid feature and enables FinOps, DevOps and Platform teams to set rough values based on historic usage, which lets development teams generate more accurate estimates."]}),"\n",(0,n.jsxs)(s.li,{children:[(0,n.jsx)(s.a,{href:"#infracost-usageyml",children:"infracost-usage.yml"}),": development teams can also use this file to provide usage values in their repos. This is a free feature. These values are merged with the centrally-defined values and take precedence over them."]}),"\n"]}),"\n",(0,n.jsx)("iframe",{width:"90%",height:"450",src:"https://www.youtube.com/embed/dZxO4XUq7UE",title:"Demo of usage costs feature",frameBorder:0,allow:"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture",allowFullScreen:!0}),"\n",(0,n.jsx)(s.h2,{id:"infracost-cloud",children:"Infracost Cloud"}),"\n",(0,n.jsx)(s.h3,{id:"predefined-values",children:"Predefined values"}),"\n",(0,n.jsx)(s.p,{children:"For all resource types, we've predefined values that attempt to set each usage-based cost as $5/month for c
1ommon configurations, helping engineers understand that these resources are not free. To see the predefined values, go to Org Settings > Usage defaults."}),"\n",(0,n.jsxs)(s.p,{children:["Disabling predefined values means that usage keys not ",(0,n.jsx)(s.a,{href:"#add-overrides",children:"overridden"})," in your usage defaults will not show costs in pull request comments and Infracost Cloud. Therefore, engineers might think these resources are free."]}),"\n",(0,n.jsx)("img",{src:(0,i.Ay)("img/infracost-cloud/usage-defaults.png"),alt:"Usage defaults in Infracost Cloud"}),"\n",(0,n.jsx)(s.h3,{id:"add-overrides",children:"Add overrides"}),"\n",(0,n.jsxs)(s.p,{children:["Predefined usage values can be overridden by creating a new usage default; simply search for a resource type and add override values (shown below). You can also set ",(0,n.jsx)(s.a,{href:"/docs/infracost_cloud/key_concepts/#projects",children:"project"})," filters so the overrides only apply to certain projects. This is useful when defining production vs non-production usage values where production values are much higher."]}),"\n",(0,n.jsx)("img",{src:(0,i.Ay)("img/infracost-cloud/usage-overrides.png"),alt:"Usage overrides in Infracost Cloud"}),"\n",(0,n.jsx)(s.h2,{id:"infracost-usageyml",children:"infracost-usage.yml"}),"\n",(0,n.jsxs)(s.p,{children:["The ",(0,n.jsx)(s.code,{children:"infracost-usage.yml"})," file lets engineers set usage values in their repos. These are merged with any values defined in ",(0,n.jsx)(s.a,{href:"#infracost-cloud",children:"Infracost Cloud"})," and take precedence over them. To use this method:"]}),"\n",(0,n.jsxs)(s.ol,{children:["\n",(0,n.jsxs)(s.li,{children:["Copy ",(0,n.jsx)(s.a,{href:"https://github.com/infracost/infracost/blob/master/infracost-usage-defaults.small.yml",children:"this file"})," into your repo and customize the required values. This predefined file attempts to set each usage-based cost as $5/month for common configurations, helping engineers understand that these resources are not free."]}),"\n",(0,n.jsxs)(s.li,{children:["Reference the file from your ",(0,n.jsx)(s.a,{href:"/docs/features/config_file/",children:(0,n.jsx)(s.code,{children:"infracost.yml"})})," so ",(0,n.jsx)(s.code,{children:"infracost scan"})," picks it up. Set ",(0,n.jsx)(s.code,{children:"usage_file"})," either at the top level (applies to all projects) or per project:"]}),"\n"]}),"\n",(0,n.jsx)(s.pre,{children:(0,n.jsx)(s.code,{className:"language-yaml",children:'version: "0.3"\nusage_file: infracost-usage.yml\n\nprojects:\n - path: .\n'})}),"\n",(0,n.jsx)(s.p,{children:"Per-project usage files use the same key inside the project entry:"}),"\n",(0,n.jsx)(s.pre,{children:(0,n.jsx)(s.code,{className:"language-yaml",children:'version: "0.3"\nprojects:\n - path: dev\n usage_file: dev-usage.yml\n - path: prod\n usage_file: prod-usage.yml\n'})}),"\n",(0,n.jsxs)(s.p,{children:["This ",(0,n.jsx)(s.code,{children:"infracost-usage.yml"})," file does not currently support ",(0,n.jsx)(s.a,{href:"/docs/features/usage_based_resources/#add-overrides",children:"project filters"}),". However, you can set values for specific resources. For example, you can set values for ",(0,n.jsx)(s.code,{children:"aws_lambda_function.my_function"})," as opposed to the ",(0,n.jsx)(s.code,{children:"aws_lambda_function"})," resource type that applies to all Lambda functions. This is useful when dealing with outlier resources that require customization."]}),"\n",(0,n.jsxs)(a,{children:[(0,n.jsx)("summary",{children:"Customizing usage values for individual resources"}),(0,n.jsxs)(s.p,{children:["The following ",(0,n.jsx)(s.code,{children:"infracost-usage.yml"})," file demonstrates how values for individual resources can be customized:"]}),(0,n.jsx)(s.pre,{children:(0,n.jsx)(s.code,{className:"language-yml",children:"version: 0.1\n# Defaults applied to all resources of this type\nresource_type_default_usage:\n aws_dynamodb_table:\n storage_gb: 1000 # Set in all DynamoDB table resources\n\n# Values applied to individual resources\nresource_usage:\n aws_dynamodb_table.my_table:\n monthly_write_request_units: 200 # Merged with default that defines storage_gb, so both attributes are set for this resource\n\n aws_dynamodb_table.my_other_table:\n storage_gb: 50 # Overrides the default\n\n # Use the full path of the resource for modules (same value that Infracost outputs in the Name column)\n module.my_module.aws_dynamodb_table.my_table:\n storage_gb: 1000\n"})}),(0,n.jsx)(s.h4,{id:"resource-arraysmaps",children:"Resource arrays/maps"}),(0,n.jsxs)(s.p,{children:["The wildcard character ",(0,n.jsx)(s.code,{children:"[*]"})," can be used for resource arrays (resources with ",(0,n.jsxs)(s.a,{href:"https://www.terraform.io/docs/language/meta-arguments/count.html",children:[(0,n.jsx)(s.code,{children:"count"})," meta-argument"]}),") and resource maps (resources with ",(0,n.jsxs)(s.a,{href:"https://www.terraform.io/docs/language/meta-arguments/for_each.html",children:[(0,n.jsx)(s.code,{children:"for_each"})," meta-argument"]}),"), such as AWS CloudWatch Log Groups. Infracost will apply the usage values individually to each element of the array/map (they all get the same values). If both an array element such as ",(0,n.jsx)(s.code,{children:"this[0]"})," (or map element such as ",(0,n.jsx)(s.code,{children:'this["foo"]'}),") and ",(0,n.jsx)(s.code,{children:"[*]"})," are specified for a resource, only the array/map element's usage will be applied to that resource. This enables you to define default values using ",(0,n.jsx)(s.code,{children:"[*]"})," and override specific elements using their index or key."]}),(0,n.jsxs)(o.A,{defaultValue:"using-array-wildcard",values:[{label:"Using array or map wildcard",value:"using-array-wildcard"},{label:"Array without wildcard",value:"array-without-wildcard"},{label:"Map without wildcard",value:"map-without-wildcard"}],children:[(0,n.jsx)(l.A,{value:"using-array-wildcard",children:(0,n.jsx)(s.pre,{children:(0,n.jsx)(s.code,{className:"language-yml",children:"version: 0.1\nresource_usage:\n aws_cloudwatch_log_group.my_group[*]:\n storage_gb: 1000\n monthly_data_ingested_gb: 1000\n monthly_data_scanned_gb: 200\n mod.my_module[*].aws_cloudwatch_log_group.my_group[*]:\n storage_gb: 1000\n monthly_data_ingested_gb: 1000\n monthly_data_scanned_gb: 200\n"})})}),(0,n.jsx)(l.A,{value:"array-without-wildcard",children:(0,n.jsx)(s.pre,{children:(0,n.jsx)(s.code,{className:"language-yml",children:"version: 0.1\nresource_usage: \n aws_cloudwatch_log_group.my_group[0]:\n storage_gb: 1000\n monthly_data_ingested_gb: 1000\n monthly_data_scanned_gb: 200\n\n aws_cloudwatch_log_group.my_group[1]:\n storage_gb: 1000\n monthly_data_ingested_gb: 1000\n monthly_data_scanned_gb: 200\n\n aws_cloudwatch_log_group.my_group[3]:\n storage_gb: 1000\n monthly_data_ingested_gb: 1000\n monthly_data_scanned_gb: 200\n\n mod.my_mod[0].aws_cloudwatch_log_group.my_group[0]:\n storage_gb: 1000\n monthly_data_ingested_gb: 1000\n monthly_data_scanned_gb: 200\n\n mod.my_mod[1].aws_cloudwatch_log_group.my_group[0]:\n storage_gb: 1000\n monthly_data_ingested_gb: 1000\n monthly_data_scanned_gb: 200\n"})})}),(0,n.jsx)(l.A,{value:"map-without-wildcard",children:(0,n.jsx)(s.pre,{children:(0,n.jsx)(s.code,{className:"language-yml",children:'version: 0.1\nresource_usage:\n aws_cloudwatch_log_group.my_group["foo"]:\n storage_gb: 1000\n monthly_data_ingested_gb: 1000\n monthly_data_scanned_gb: 200\n\n mod.my_mod["bar"].aws_cloudwatch_log_group.my_group["foo"]:\n storage_gb: 1000\n monthly_data_ingested_gb: 1000\n monthly_data_scanned_gb: 200\n'})})})]}),(0,n.jsx)(s.h4,{id:"ec2-reserved-instances",children:"EC2 reserved instances"}),(0,n.jsxs)(s.p,{children:["What-if analysis can be done on AWS EC2 Reserved Instances (RI) using the usage file. The RI type, term and payment option can be defined as shown below, to quickly get a monthly cost estimate. This works with ",(0,n.jsx)(s.code,{children:"aws_instance"})," as well as ",(0,n.jsx)(s.code,{children:"aws_eks_node_group"})," and ",(0,n.jsx)(s.code,{children:"aws_autoscaling_group"})," as they also create EC2 instances. Let us know how you'd like Infracost to show the upfront costs by ",(0,n.jsx)(s.a,{href:"https://github.com/infracost/infracost/issues/",children:"creating a GitHub issue"}),"."]}),(0,n.jsx)(s.pre,{children:(0,n.jsx)(s.code,{className:"language-yml",children:"version: 0.1\nresource_usage: \n aws_instance.my_instance:\n operating_system: linux # Override the operating system of the instance, can be: linux, windows, suse, rhel.\n reserved_instance_type: standard # Offering class for Reserved Instances. Can be: convert
1ible, standard.\n reserved_instance_term: 1_year # Term for Reserved Instances. Can be: 1_year, 3_year.\n reserved_instance_payment_option: all_upfront # Payment option for Reserved Instances. Can be: no_upfront, partial_upfront, all_upfront.\n"})})]})]})}function m(e={}){const{wrapper:s}={...(0,t.R)(),...e.components};return s?(0,n.jsx)(s,{...e,children:(0,n.jsx)(f,{...e})}):f(e)}},19365(e,s,a){a.d(s,{A:()=>i});a(96540);var r=a(34164),n=a(47751);const t="tabItem_Ymn6";var o=a(74848);function l(e){let s=e.children,a=e.className,n=e.hidden;return(0,o.jsx)("div",{role:"tabpanel",className:(0,r.A)(t,a),hidden:n,children:s})}function i(e){let s=e.children,a=e.className,r=e.value;const t=(0,n.uc)(),i=t.selectedValue,c=t.lazy,u=r===i;return!u&&c?null:(0,o.jsx)(l,{className:a,hidden:!u,children:s})}},4865(e,s,a){a.d(s,{A:()=>m});a(96540);var r=a(34164),n=a(17559),t=a(47751),o=a(23104),l=a(92303);const i="tabList__CuJ",c="tabItem_LNqP";var u=a(74848);function d(e){let s=e.className;const a=(0,t.uc)(),n=a.selectedValue,l=a.selectValue,i=a.tabValues,d=a.block,h=[],f=(0,o.a_)().blockElementScrollPositionUntilNextRender,m=e=>{const s=e.currentTarget,a=h.indexOf(s),r=i[a].value;r!==n&&(f(s),l(r))},g=e=>{var s;let a=null;switch(e.key){case"Enter":m(e);break;case"ArrowRight":{var r;const s=h.indexOf(e.currentTarget)+1;a=null!=(r=h[s])?r:h[0];break}case"ArrowLeft":{var n;const s=h.indexOf(e.currentTarget)-1;a=null!=(n=h[s])?n:h[h.length-1];break}}null==(s=a)||s.focus()};return(0,u.jsx)("ul",{role:"tablist","aria-orientation":"horizontal",className:(0,r.A)("tabs",{"tabs--block":d},s),children:i.map(e=>{let s=e.value,a=e.label,t=e.attributes;return(0,u.jsx)("li",Object.assign({role:"tab",tabIndex:n===s?0:-1,"aria-selected":n===s,ref:e=>{h.push(e)},onKeyDown:g,onClick:m},t,{className:(0,r.A)("tabs__item",c,null==t?void 0:t.className,{"tabs__item--active":n===s}),children:null!=a?a:s}),s)})})}function h(e){let s=e.children;return(0,u.jsx)("div",{className:"margin-top--md",children:s})}function f(e){let s=e.className,a=e.children;return(0,u.jsxs)("div",{className:(0,r.A)(n.G.tabs.container,"tabs-container",i),children:[(0,u.jsx)(d,{className:s}),(0,u.jsx)(h,{children:a})]})}function m(e){const s=(0,l.A)(),a=(0,t.OC)(e);return(0,u.jsx)(t.O_,{value:a,children:(0,u.jsx)(f,{className:e.className,children:(0,t.vT)(e.children)})},String(s))}},31682(e,s,a){function r(e,s){return void 0===s&&(s=(e,s)=>e===s),e.filter((a,r)=>e.findIndex(e=>s(e,a))!==r)}function n(e,s){const a={};let r=0;for(const n of e){const e=s(n,r);null!=a[e]||(a[e]=[]),a[e].push(n),r+=1}return a}
1a.d(s,{$z:()=>n,XI:()=>r})},47751(e,s,a){a.d(s,{OC:()=>m,O_:()=>_,uc:()=>p,vT:()=>u});var r=a(96540),n=a(56347),t=a(205),o=a(57485),l=a(70679),i=a(31682),c=a(74848);function u(e){return r.Children.toArray(e).filter(e=>"\n"!==e)}function d(e){const s=e.values,a=e.children;return(0,r.useMemo)(()=>{const e=null!=s?s:function(e){return r.Children.toArray(e).flatMap(e=>{if(!e)return[];if((0,r.isValidElement)(e)&&function(e){const s=e.props;return!!s&&"object"==typeof s&&"value"in s}(e))return[e];const s="string"==typeof e.type?e.type:e.type.name;throw new Error("Docusaurus error: Bad <Tabs> child <"+s+'>: all children of the <Tabs> component should be <TabItem>, and every <TabItem> should have a unique "value" prop.\nIf you do not want to pass on a "value" prop to the direct children of <Tabs>, you can also pass an explicit <Tabs values={...}> prop.')}).map(e=>{let s=e.props;return{value:s.value,label:s.label,attributes:s.attributes,default:s.default}})}(a);return function(e){const s=(0,i.XI)(e,(e,s)=>e.value===s.value);if(s.length>0)throw new Error('Docusaurus error: Duplicate values "'+s.map(e=>"'"+e.value+"'").join(", ")+'" found in <Tabs>. Every value needs to be unique.')}(e),e},[s,a])}function h(e){let s=e.value;return e.tabValues.some(e=>e.value===s)}function f(e){let s=e.queryString,a=void 0!==s&&s,t=e.groupId;const l=(0,n.W6)(),i=function(e){let s=e.queryString,a=void 0!==s&&s,r=e.groupId;if("string"==typeof a)return a;if(!1===a)return null;if(!0===a&&!r)throw new Error('Docusaurus error: The <Tabs> component groupId prop is required if queryString=true, because this value is used as the search param name. You can also provide an explicit value such as queryString="my-search-param".');return null!=r?r:null}({queryString:a,groupId:t});return[(0,o.aZ)(i),(0,r.useCallback)(e=>{if(!i)return;const s=new URLSearchParams(l.location.search);s.set(i,e),l.replace(Object.assign({},l.location,{search:s.toString()}))},[i,l])]}function m(e){var s,a;const n=e.defaultValue,o=e.queryString,i=void 0!==o&&o,c=e.groupId,u=d(e),m=(0,r.useState)(()=>function(e){var s;let a=e.defaultValue,r=e.tabValues;if(0===r.length)throw new Error("Docusaurus error: the <Tabs> component requires at least one <TabItem> children component");if(a){if(!h({value:a,tabValues:r}))throw new Error('Docusaurus error: The <Tabs> has a defaultValue "'+a+'" but none of its children has the corresponding value. Available values are: '+r.map(e=>e.value).join(", ")+". If you intend to show no default tab, use defaultValue={null} instead.");return a}const n=null!=(s=r.find(e=>e.default))?s:r[0];if(!n)throw new Error("Unexpected error: 0 tabValues");return n.value}({defaultValue:n,tabValues:u})),g=m[0],p=m[1],_=f({queryString:i,groupId:c}),v=_[0],y=_[1],b=function(e){const s=function(e){return e?"docusaurus.tab."+e:null}(e.groupId),a=(0,l.Dv)(s),n=a[0],t=a[1];return[n,(0,r.useCallback)(e=>{s&&t.set(e)},[s,t])]}({groupId:c}),w=b[0],x=b[1],j=(()=>{const e=null!=v?v:w;return h({value:e,tabValues:u})?e:null})();(0,t.A)(()=>{j&&p(j)},[j]);return{selectedValue:g,selectValue:(0,r.useCallback)(e=>{if(!h({value:e,tabValues:u}))throw new Error("Can't select invalid tab value="+e);p(e),y(e),x(e)},[y,x,u]),tabValues:u,lazy:null!=(s=e.lazy)&&s,block:null!=(a=e.block)&&a}}const g=(0,r.createContext)(null);function p(){const e=r.useContext(g);if(!e)throw new Error("useTabsContext() must be used within a Tabs component");return e}function _(e){return(0,c.jsx)(g.Provider,{value:e.value,children:e.children})}},28453(e,s,a){a.d(s,{R:()=>o,x:()=>l});var r=a(96540);const n={},t=r.createContext(n);function o(e){const s=r.useContext(t);return r.useMemo(function(){return"function"==typeof e?e(s):{...s,...e}},[s,e])}function l(e){let s;return s=e.disableParentContext?"function"==typeof e.components?e.components(n):e.components||n:o(e.components),r.createElement(t.Provider,{value:s},e.children)}}}]);
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.